Missing function name in exprtk symbol table post successful function compositor
Asked Answered
W

1

5

I have the following function that takes a symbol table and adds a composited function to the symbol table:

void add_function(exprtk::symbol_table<double>& symtab) {

   using compositor_t = exprtk::function_compositor<double>;
   using function_t   = typename compositor_t::function;

   compositor_t compositor(symtab);

   const bool res = compositor.add(
      function_t("incrementor")
      .var("x")
      .expression
      ( " x + 1; " ));

   if (!res) {
      printf("comp err!");
   }
}

Later on I call the add function and then compile an expression that uses the sym table

using symbol_table_t = exprtk::symbol_table<double>;
using expression_t   = exprtk::expression<double>;
using parser_t       = exprtk::parser<double>;

double x = 123.0;

symbol_table_t symbol_table;
expression_t   expression;
parser_t       parser;

symbol_table.add_variable("x", x);

add_function(symbol_table);

expression.register_symbol_table(symbol_table);

const std::string program = "incrementor(x)";

if (!parser.compile(program,expression)) {
   printf("err: %s\n",parser.error().c_str());
   return;
}

However the parser.compile returns false, and I get the following error:

Error: ERR232 - Undefined symbol: 'incrementor'

I've checked the sym table using a debugger and the incrementor function is not there. Not sure why or how the incrementor symbol disappears between the call to add_function and compile.

Wiltz answered 28/10 at 1:13 Comment(1)
You add the function to the compositor, but I don't see you adding to a symbol table.Lengthen
H
7

In the add_function you correctly pass the symtab during the function_compositor constructor, so that any new composited functions have their references added to the symtab

The main issue you're seeing occurs at the closing brace of the add_function.

At that point the destructors for all the function local defined instances are invoked. This includes the destructor for the function_compositor instance.

During destruction a protection mechanism within the function_compositor kicks in and removes all composited function references that have been created from the symbol tables they have been registered with. This is done so that no expressions can be compiled that would be referencing composited functions that no longer exist causing undefined behaviour.

This means that just before the closing brace, using the debugger you would indeed see the reference to the "incrementor" composited function, however just after that it will have been removed.

Subsequently when the expression is compiled, the symbol name "incrementor" will be undefined resulting in the given error diagnostic.

In the ExprTk readme.txt there is the following note:

The life-time of objects  registered with or created  from a
specific symbol-table must span at least the lifetime of the
symbol  table instance  and all  compiled expressions  which
utilise  objects,  such  as  variables,  strings,   vectors,
function compositor functions and functions of that  symbol-
table, otherwise the result will be undefined behaviour.

As a side note, a function_compositor is fundamentally an object factory - similar to a parser instance. As such it should only be constructed once during the life-time of the process and destructed only after it will no longer be required. It should not be constructed and destructed on an ad-hoc basis.


My recommendation is to couple both the symbol_table and function_compositor instances so that they are destructed at the same point.

Hasidism answered 29/10 at 22:24 Comment(1)
thanks! that makes sense, we've refactored the code to not create and destroy the compositor on each call and it's working well.Wiltz

© 2022 - 2024 — McMap. All rights reserved.