I have expressions where I'd like to use a binded variable, but don't want the variable to be assigned new values - sort of like if it were a const variable in C++.
Tried using the add_constant method from symbol_table, however when I change the value of the variable between calls to value, it seems to retain the value of the variable when the expression was first compiled.
using symbol_table_t = exprtk::symbol_table<float>;
using expression_t = exprtk::expression<float>;
using parser_t = exprtk::parser<float>;
float x = 1.0f;
symbol_table_t symbol_table;
symbol_table.add_constant("x",x);
expression_t expression;
expression.register_symbol_table(symbol_table);
parser_t parser;
expression_string = "x + 3";
parser.compile(expression_string, expression);
expression.value(); // Get 4 which is ok
x = 3;
expression.value(); // Expect 6, but get 4
parser.compile("x := x/3+1", expression); // Want this to fail compilation due to assigning
Is there a way to bind the a variable via the symb table but not allow it to be assigned to, but also make sure when the expression is valued to use its most recent or current value?
Initially: what you want is equivalent to a const ref
in C++, what you're currently doing
is equivalent to a const
variable.
The behaviour you are seeing is due to the const-folding optimisation pass during compilation.
What is happening is that you've registered the variable x
to the
symbol_table
instance, as a constant value.
When ExprTk compiles the expression it replaces the occurrences of
x with the value captured from x at the time it was registered (or
bound) to the symbol_table
, which is one.
Then during the const-folding pass, the compiler sees
an internal sub expression of "1 + 3"
, and says:
"Hey! I can compute the result of that sub-expression right here,
right now, why not reduce the cost by eliminating the addition
operation and simply replace '1+3'
with the value of 4 and call it a
day"
What you really want, is to have a variable registered with the
symbol_table
, have it available in the expression as a normal variable
(aka having its value loaded every time the expression is evaluated),
but somehow communicate to the compiler that you don't want any values
assigned to the variable - essentially making it immutable, and in the
case there is such an expression to have an error raised during
compilation.
ExprTk probives this capability, via setting up symbol table instances as being immutable, like so:
symbol_table_t my_immutable_symbol_table(
symbol_table_t::symtab_mutability_type::e_immutable);
In the above symbol_table instance any variable registered via the add_variable/add_vector/add_string will be immutable in the expressions they are called from. The following is more complete example, demonstrating a mix of normal and immutable symbol tables:
using T = double;
using symbol_table_t = typedef exprtk::symbol_table<T>;
using expression_t = typedef exprtk::expression<T>;
using parser_t = typedef exprtk::parser<T>;
T x = 1.1;
T y = 2.2;
T z = 3.3;
T w = 4.4;
symbol_table_t mutable_symbol_table;
symbol_table_t immutable_symbol_table( symbol_table_t::symtab_mutability_type::e_immutable);
mutable_symbol_table.add_variable("x", x);
mutable_symbol_table.add_variable("y", y);
immutable_symbol_table.add_variable("z", z);
immutable_symbol_table.add_variable("w", w);
expression_t expression;
expression.register_symbol_table(immutable_symbol_table);
expression.register_symbol_table(mutable_symbol_table );
parser_t parser;
const std::vector<std::string> expressions =
{
"x := y + (z / w)", // ok - will compile
"y := y / x + (z / w)", // ok - will compile
"z := y + x - w", // Error - will not compile
"z == (w := y / x)", // Error - will not compile
};
for (const auto& expression_str : expressions)
{
if (!parser.compile(expression_str, expression))
{
printf("Error: %s\n"mparser.error().c_str());
continue;
}
// Modify all the variables from both the immutable
// and mutable symbol tables
x += 1.1;
y += 2.2;
z += 3.3;
w += 4.4;
expression.value();
}