ExprTK: why is this expression value not expected?

114 views Asked by At

The piece of code below is to evaluate expression string "x3 - y".

The expression should be evaluated as2 cause x3 should be evaluated as 3 and y should be 1. However, it's evaluated as -1

I tried to print the value of x3 and y. It prints 3 and 1 for x3 and y, which is expected.

I also tried to replace add_variable with method create_variable, and it output correct result.

so what's wrong with the piece of code?

void test() {
    typedef exprtk::symbol_table<double> symbol_table_t;
    typedef exprtk::expression<double> expression_t;
    typedef exprtk::parser<double> parser_t;

    std::string expression_str = "x3 - y";
    symbol_table_t symbol_table;
    double y = 1;
    std::vector<double> xs;
    for (int i = 0; i < 5; i++) {
        xs.push_back(i);
        // symbol_table.create_variable("x"+ std::to_string(i), xs[i]); The output is correct if use create_variable
        symbol_table.add_variable("x"+ std::to_string(i), xs[i]);
    }
    symbol_table.add_variable("y", y);

    std::cout << symbol_table.get_variable("x3")->value() << " " << symbol_table.get_variable("y")->value() << std::endl; // x3 == 3, y == 1
    expression_t expression;
    expression.register_symbol_table(symbol_table);

    parser_t tmp_parser;
    tmp_parser.compile(expression_str, expression);

    std::cout << expression.value(); // output is -1
}
1

There are 1 answers

0
ExprTk Developer On

The answer provided by @IgorTandetnik is correct. The method symbol_table.add_variable takes references to the variables passed to it. By adding more variables to the vector xs via the push_back method, a resize of the vector may occur, which will invalidate the previous references taken from xs that are present in the symbol_table instance.

From the ExprTk readme.txt Section 10.1 there is the following code listing that denotes the various ways variables that have been added to a symbol_table can be invalidated:

typedef exprtk::symbol_table<double> symbol_table_t;
typedef exprtk::expression<double>   expression_t;

symbol_table_t symbol_table;
expression_t   expression;

std::deque<double > y {1.1, 2.2, 3.3};
std::vector<double> z {4.4, 5.5, 6.6};
double* w = new double(123.456);

{
   double x = 123.4567;
   symbol_table.add_variable("x", x);
}             // Reference to variable x has been invalidated


symbol_table.add_variable("y", y.back());

y.pop_back(); // Reference to variable y has been invalidated


symbol_table.add_variable("z", z.front());

z.erase(z.begin());
              // Reference to variable z has been invalidated

symbol_table.add_variable("w", *w);

delete w;     // Reference to variable w has been invalidated

const std::string expression_str = "x + y / z * w";

              // Compilation of expression will succeed
parser.compile(expression_str,expression);

expression.value();
             // Evaluation will result in undefined behaviour
             // due to 'x' and 'w' having been destroyed.

symbol_table.get_variable("x")->ref() = 135.791;
             // Assignment will result in undefined behaviour