diff --git a/exprtk.hpp b/exprtk.hpp index 4e1188c..17fb1e8 100644 --- a/exprtk.hpp +++ b/exprtk.hpp @@ -8344,6 +8344,23 @@ namespace exprtk { public: + typedef range_pack range_pack_t; + + struct range_type + { + range_type() + : range(0), + data (0), + size (0), + type_size(0) + {} + + range_pack_t* range; + void* data; + std::size_t size; + std::size_t type_size; + }; + typedef type_store type_store_t; typedef expression_node* expression_ptr; typedef variable_node variable_node_t; @@ -8353,13 +8370,11 @@ namespace exprtk typedef vector_elem_node_t* vector_elem_node_ptr_t; typedef vector_node_t* vector_node_ptr_t; typedef range_interface range_interface_t; - typedef range_pack range_pack_t; typedef std::pair branch_t; typedef std::pair void_t; - typedef std::pair range_t; typedef std::vector tmp_vs_t; typedef std::vector typestore_list_t; - typedef std::vector range_list_t; + typedef std::vector range_list_t; generic_function_node(GenericFunction* func, const std::vector& arg_list) @@ -8383,8 +8398,7 @@ namespace exprtk { expr_as_vec1_store_.resize(arg_list_.size(),T(0)); - range_list_.resize(arg_list_.size(), - range_t((range_pack_t*)0,void_t((void*)0,0))); + range_list_.resize(arg_list_.size(),range_type()); for (std::size_t i = 0; i < arg_list_.size(); ++i) { @@ -8414,8 +8428,9 @@ namespace exprtk ts.data = reinterpret_cast(const_cast(sbn->base())); ts.type = type_store_t::e_string; - range_list_[i].second.first = ts.data; - range_list_[i].second.second = sizeof(char); + range_list_[i].data = ts.data; + range_list_[i].size = ts.size; + range_list_[i].type_size = sizeof(char); if ( is_string_range_node (arg_list_[i]) || @@ -8433,10 +8448,10 @@ namespace exprtk { ts.size = rp.n1_c.second - rp.n0_c.second + 1; ts.data = static_cast(ts.data) + rp.n0_c.second; - range_list_[i].first = reinterpret_cast(0); + range_list_[i].range = reinterpret_cast(0); } else - range_list_[i].first = &(ri->range_ref()); + range_list_[i].range = &(ri->range_ref()); } } else if (is_variable_node(arg_list_[i])) @@ -8484,11 +8499,13 @@ namespace exprtk { if (function_) { - populate_value_list(); - return (*function_)(fwd_typestore_list_); + if (populate_value_list()) + { + return (*function_)(fwd_typestore_list_); + } } - else - return std::numeric_limits::quiet_NaN(); + + return std::numeric_limits::quiet_NaN(); } inline typename expression_node::node_type type() const @@ -8498,22 +8515,31 @@ namespace exprtk private: - inline void populate_value_list() const + inline bool populate_value_list() const { for (std::size_t i = 0; i < branch_.size(); ++i) { expr_as_vec1_store_[i] = branch_[i].first->value(); - if (range_list_[i].first) + if (range_list_[i].range) { - range_pack_t& rp = (*range_list_[i].first); + range_pack_t& rp = (*range_list_[i].range); + std::size_t r0 = 0; + std::size_t r1 = 0; - typestore_list_[i].size = (rp.cache.second - rp.cache.first) + 1; - typestore_list_[i].data = static_cast(range_list_[i].second.first) + (rp.cache.first * range_list_[i].second.second); + if (rp(r0,r1,range_list_[i].size)) + { + typestore_list_[i].size = (rp.cache.second - rp.cache.first) + 1; + typestore_list_[i].data = static_cast(range_list_[i].data) + (rp.cache.first * range_list_[i].type_size); + } + else + return false; } } fwd_typestore_list_ = typestore_list_; + + return true; } GenericFunction* function_; @@ -12018,6 +12044,7 @@ namespace exprtk inline virtual T operator()(const std::vector&) { + exprtk_debug(("ivararg_function::operator() - Operator has not been overriden.\n")); return std::numeric_limits::quiet_NaN(); } @@ -12044,6 +12071,7 @@ namespace exprtk inline virtual T operator()(parameter_list_t&) { + exprtk_debug(("igeneric_function::operator() - Operator has not been overriden.\n")); return std::numeric_limits::quiet_NaN(); } diff --git a/readme.txt b/readme.txt index aafc6d8..8aaa0c2 100644 --- a/readme.txt +++ b/readme.txt @@ -2,10 +2,10 @@ C++ Mathematical Expression Toolkit Library [00 - INTRODUCTION] The C++ Mathematical Expression Toolkit Library (ExprTk) is a simple -to use, easy to integrate and extremely efficient mathematical -expression parsing and evaluation engine. The parsing engine supports -numerous forms of functional and logic processing semantics and is -easily extendible. +to use, easy to integrate and extremely efficient run-time +mathematical expression parsing and evaluation engine. The parsing +engine supports numerous forms of functional and logic processing +semantics and is easily extendible. @@ -15,14 +15,14 @@ arithmetic operations, functions and processes: (00) Basic operators: +, -, *, /, %, ^ - (01) Functions: abs, avg, ceil, clamp, equal, erf, erfc, exp, - expm1, floor, frac, log, log10, log1p, log2, - logn, max, min, mul, ncdf, nequal, root, round, - roundn, sgn, sqrt, sum, swap, trunc + (01) Functions: abs, avg, ceil, clamp, equal, erf, erfc, exp, + expm1, floor, frac, log, log10, log1p, log2, + logn, max, min, mul, ncdf, nequal, root, + round, roundn, sgn, sqrt, sum, swap, trunc - (02) Trigonometry: acos, acosh, asin, asinh, atan, atanh, atan2, - cos, cosh, cot, csc, sec, sin, sinc, sinh, - tan, tanh, hypot, rad2deg, deg2grad, deg2rad, + (02) Trigonometry: acos, acosh, asin, asinh, atan, atanh, atan2, + cos, cosh, cot, csc, sec, sin, sinc, sinh, + tan, tanh, hypot, rad2deg, deg2grad, deg2rad, grad2deg (03) Equalities & @@ -899,7 +899,7 @@ zero. The following are examples of vector definitions: (c) Initialise all values to given expression var x[3] := [123 + 3y + sin(w/z)]; - (d) Initialise the first two values, other elements to zero + (d) Initialise the first two values, all other elements to zero var x[3] := { 1 + x[2], sin(y[0] / x[]) + 3 }; (e) Initialise the first three (all) values @@ -1019,7 +1019,8 @@ ExprTk provides a means whereby custom functions can be defined and utilized within expressions. The concept requires the user to provide a reference to the function coupled with an associated name that will be invoked within expressions. Function can take in numerous -inputs but will always return one value. +inputs but will always return a single value of the underlying numeric +type. During expression compilation when required the reference to the function will be obtained from the associated symbol_table and be @@ -1089,7 +1090,9 @@ parameters and their views are as follows: (2) vector - vector_view (3) string - string_view -The following example defines a generic function called 'too': +The above denoted type views provide non-const reference-like access +to each parameter. The following example defines a generic function +called 'too': template struct too : public exprtk::igeneric_function @@ -1104,25 +1107,134 @@ The following example defines a generic function called 'too': { for (std::size_t i = 0; i < parameters.size(); ++i) { + ... } return T(0); } }; -In the above example, the parameter_list_t type is a type of -std::vector of type_store. Each type_store instance has a member -called 'type' which holds the enumeration pertaining the underlying -type of the type_store. There are three type enumerations: +In the above example, the input 'parameters' to the function operator, +parameter_list_t, is a type of std::vector of type_store. Each +type_store instance has a member called 'type' which holds the +enumeration pertaining the underlying type of the type_store. There +are three type enumerations: - (1) e_scalar - variables, vector elements, general expressions - eg: x, y, z, vec[3x + 1], 2x + 3 + (1) e_scalar - literals, variables, vector elements, expressions + eg: 123.456, x, vec[3x + 1], 2x + 3 (2) e_vector - vectors, vector expressions eg: vec1, 2 * vec1 + vec2 / 3 - (3) e_string - string, string literal and range variants of both - eg: 'AString', s0, 'AString'[x:y], s1[1+x:] + (3) e_string - strings, string literals and range variants of both + eg: 'AString', s0, 'AString'[x:y], s1[1 + x:] + + +Each of the parameters can be accessed using its designated view. A +typical loop for processing the parameters is as follows: + + inline T operator()(parameter_list_t& parameters) + { + typedef typename exprtk::igeneric_function::generic_type + generic_type; + + typedef typename generic_type::scalar_view scalar_t; + typedef typename generic_type::vector_view vector_t; + typedef typename generic_type::string_view string_t; + + for (std::size_t i = 0; i < parameters.size(); ++i) + { + generic_type& gt = parameters[i]; + + if (generic_type::e_scalar == gt.type) + { + scalar_t x(gt); + ... + } + else if (generic_type::e_vector == gt.type) + { + vector_t vector(gt); + ... + } + else if (generic_type::e_string == gt.type) + { + string_t string(gt); + ... + } + } + + return T(0); + } + + +Most often than not a custom generic function will require a specific +sequence of parameters, rather than some arbitrary sequence of types. +In those situations, ExprTk can perform compile-time type checking to +validate that function invocations are carried out using the correct +sequence of parameters. Furthermore performing the checks at compile +-time rather than at run-time (aka every time the function is invoked) +will result expression evaluation performance gains. + +Compile-time type checking can be requested by passing a string +representing the desired parameter sequence to the constructor of the +igeneric_function. The following example demonstrates how this can be +done: + + template + struct too : public exprtk::igeneric_function + { + typedef typename exprtk::igeneric_function::parameter_list_t + parameter_list_t; + + too() + : exprtk::igeneric_function("SVTT") + {} + + inline T operator()(parameter_list_t& parameters) + { + ... + } + }; + + +In the example above the custom generic function 'too' expects +exactly four parameters in the following sequence: + + (a) String + (b) Vector + (c) Scalar + (d) Scalar + + +One further refinement to the type checking facility is possibility of +a variable number of trailing common types which can be accomplished +by using a wildcard '*' at the end of the sequence definition. + + template + struct too : public exprtk::igeneric_function + { + typedef typename exprtk::igeneric_function::parameter_list_t + parameter_list_t; + + too() + : exprtk::igeneric_function("SVTTV*") + {} + + inline T operator()(parameter_list_t& parameters) + { + ... + } + }; + + +In the example above the generic function 'too' expects at least five +parameters in the following sequence: + + (a) String + (b) Vector + (c) Scalar + (d) Scalar + (e) One or more vectors (4) function_compositor @@ -1173,10 +1285,14 @@ The following demonstrates how all the pieces are put together: foo f; boo b; + too t; symbol_table_t symbol_table; + compositor_t compositor(symbol_table); + symbol_table.add_function("foo",f); symbol_table.add_function("boo",b); + symbol_table.add_function("too",t); compositor .add(function_t() @@ -1189,7 +1305,11 @@ The following demonstrates how all the pieces are put together: expression.register_symbol_table(symbol_table); std::string expression_str = - "foo(1,2,3) + boo(1) / boo(1/2,2/3,3/4,4/5) + koo(3,4)"; + " if (foo(1,2,3) + boo(1) > boo(1/2,2/3,3/4,4/5)) " + " koo(3,4); " + " else " + " too(2 * v1 + v2 / 3, 'abcdef'[2:4], 3.3); " + " "; parser_t parser; parser.compile(expression_str,expression); @@ -1357,14 +1477,14 @@ into account when using Exprtk: exist within the set of Real numbers. All other results will be of the value: Not-A-Number (NaN). - (05) Supported user defined types are numeric and string variables - and functions. + (05) Supported user defined types are numeric and string + variables, numeric vectors and functions. - (06) All reserved and key words, variable, vector and function names - are case-insensitive. + (06) All reserved words and keywords, variable, vector and function + names are case-insensitive. (07) Variable, vector and function names must begin with a letter - (A-Z or a-z), then can be comprised of any combination of + (A-Z or a-z), then can be comprised of any combination of letters, digits and underscores. (eg: x, var1 or power_func99) (08) Expression lengths and sub-expression lists are limited only by @@ -1376,8 +1496,8 @@ into account when using Exprtk: of that symbol-table, otherwise the result will be undefined behavior. - (10) Equal/Nequal are normalized equality routines, which use - epsilons of 0.0000000001 and 0.000001 for double and float + (10) Equal and Nequal are normalized equality routines, which use + epsilons of 0.0000000001 and 0.000001 for double and float types respectively. (11) All trigonometric functions assume radian input unless stated @@ -1398,8 +1518,8 @@ into account when using Exprtk: (15) The inbuilt polynomial functions can be at most of degree 12. - (16) Where appropriate constant folding optimisations may be - applied. (eg: The expression '2+(3-(x/y))' becomes '5-(x/y)') + (16) Where appropriate constant folding optimisations may be applied. + (eg: The expression '2 + (3 - (x / y))' becomes '5 - (x / y)') (17) If the strength reduction compilation option has been enabled, then where applicable strength reduction optimisations may be @@ -1422,25 +1542,27 @@ into account when using Exprtk: of the ifunction allowing it to be constant folded where appropriate. - (22) The entity relationship between symbol_table and an expression - is one-to-many. Hence the intended use case where possible is - to have a single symbol table manage the variable and function + (22) The entity relationship between symbol_table and an expression + is one-to-many. Hence the intended use case where possible is + to have a single symbol table manage the variable and function requirements of multiple expressions. (23) The common use-case for an expression is to have it compiled - only ONCE and then subsequently have it evaluated multiple + only ONCE and then subsequently have it evaluated multiple times. An extremely inefficient and suboptimal approach would be to recompile an expression from its string form every time it requires evaluating. (24) The following are examples of compliant floating point value representations: + (a) 12345 (e) -123.456 (b) +123.456e+12 (f) 123.456E-12 (c) +012.045e+07 (g) .1234 (d) 123.456f (h) -321.654E+3L (25) Expressions may contain any of the following comment styles: + 1. // .... \n 2. # .... \n 3. /* .... */