3

I can't seem to get a vector output from exprTk. I figure it should be relatively simple but in the manual I can only find how to input a vector and not how to output one.

What I currently have is the following:

typedef double T; // numeric type (float, double, mpfr etc...)
typedef exprtk::symbol_table<T> symbol_table_t;
typedef exprtk::expression<T>     expression_t;
typedef exprtk::parser<T>             parser_t;

std::string expression_string = "var z[2] := { x, y };";

T x = T(5.3);
T y = T(2.3);
std::vector<T> z;

symbol_table_t symbol_table;
symbol_table.add_variable("x", x);
symbol_table.add_variable("y", y);
symbol_table.add_vector("z", z);

expression_t expression;
expression.register_symbol_table(symbol_table);

//Check if expression is valid
parser_t parser;
if (!parser.compile(expression_string, expression))
{
    printf("Compilation error...\n");
    return;
}

T result = expression.value();
std::cout << result << std::endl;     \\returns: 5.3 as expected the first element of vector z.
std::cout << z[0] << std::endl;       \\Crashes the program 

What I want as output is just the vector z. How do I do this, or what am I doing wrong?

D.J. Klomp
  • 2,429
  • 1
  • 15
  • 30
  • 1
    I don't know how `exprtk` works but it might expect the vector to be the correct size already. If you use `std::vector z(2);`, does it still crash? If not, does it give the correct values in `z`? – NathanOliver May 26 '20 at 15:20
  • If I initialise the vector I get the "Compilation error..." message during the parser expression check. – D.J. Klomp May 26 '20 at 15:30
  • Hmm. Welp that wasn't it. Hopefully someone else will be able to help you. – NathanOliver May 26 '20 at 15:32

2 Answers2

3

As per Section 20 - Expression Return Values of the readme, One can immediately exit an expression returning any number of variables of any type simply by using the return statement.

The example provided in the documentation is as follows:

std::string expression_string =
   " if (x < y)                                   "
   "   return [x + 1,'return-call 1'];            "
   " else if (x > y)                              "
   "   return [y / 2, y + 1, 'return-call 2'];    "
   " else if (equal(x,y))                         "
   "   x + y;                                     "
   " return [x, y, x + y, x - y, 'return-call 3'] ";

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

symbol_table_t symbol_table;
expression_t   expression;
parser_t       parser;

double x = 0;
double y = 0;

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

expression.register_symbol_table(symbol_table);

parser.compile(expression_string,expression);

T result = expression.value();

if (expression.results().count())
{
   typedef exprtk::results_context<T> results_context_t;
   typedef typename results_context_t::type_store_t type_t;
   typedef typename type_t::scalar_view scalar_t;
   typedef typename type_t::vector_view vector_t;
   typedef typename type_t::string_view string_t;

   const results_context_t& results = expression.results();

   for (std::size_t i = 0; i < results.count(); ++i)
   {
      type_t t = results[i];

      switch (t.type)
      {
         case type_t::e_scalar : ...
                                 break;

         case type_t::e_vector : ...
                                 break;

         case type_t::e_string : ...
                                 break;

         default               : continue;
      }
}

Note1: Your expression will become:

var z[2] := { x, y }; return [z];

Note2: In order to have your "one-line" method you could write a simple free function that wraps the boiler plate code after the conditional and extracts the vector of your choosing (i'th result given it's of vector type) from the results list.

template <typename T>
bool get_vector(const std::size_t index, 
                const results_context_t& results,
                std::vector<T>& v)
{
   ...
}
1

I've found a method that does work, but hopefully there is a simpler 'one-line' method

std::string expression_string = "var w[2] := { x, y }; z := w";

This creates an expression local vector w ([SECTION 13 - VARIABLE, VECTOR & STRING DEFINITION]), and then assigns the value to z

As @NathanOliver mentioned, std::vector<T> z(2); is also needed


"var z[2] := { x, y };" doesn't work as it is an illegal redefinition of the z variable, due to the use of the var statement.

Note that "z := {x, y}" will also not work as this does not appear to be a valid assignment expression for a vector


Output (with debug enabled):

prev[var] --> curr[var]
prev[var] --> curr[w]
prev[2] --> curr[]]
prev[x] --> curr[,]
prev[y] --> curr[}]
parse_define_vector_statement() - INFO - Added new local vector: w[2]
activate_side_effect() - caller: parse_define_vector_statement()
parse_corpus(00) Subexpr: var w[2] := { x, y };
parse_corpus(00) - Side effect present: true
-------------------------------------------------
prev[z] --> curr[:=]
prev[:=] --> curr[w]
prev[w] --> curr[]
activate_side_effect() - caller: lodge_assignment()
parse_corpus(01) Subexpr: z := w
parse_corpus(01) - Side effect present: true
-------------------------------------------------
5.3
5.3
Adrian
  • 1,092
  • 1
  • 10
  • 19
  • Thanks that works, I will keep it open for a little bit longer, hopping for the 'one-line' method. If that is not forthcoming I will accept this answer. – D.J. Klomp May 28 '20 at 10:54