codeplea / tinyexpr

tiny recursive descent expression parser, compiler, and evaluation engine for math expressions
https://codeplea.com/tinyexpr
zlib License
1.61k stars 245 forks source link

Invalid conversion for custom function in Arduino #110

Closed pbdev001 closed 8 months ago

pbdev001 commented 8 months ago

Hi. I´m using Arduino 1.8 IDE to test tinyexpr. All goes well with standard variable evaluation. When I try to use a custom function, just copying the example from README, I get an error.

This is my complete code:

#include "tinyexpr.h"

double my_sum(double a, double b) {
    /* Example C function that adds two numbers together. */
    return a + b;
}

te_variable vars[] = { {"mysum", my_sum, TE_FUNCTION2} };

void setup() {
  te_expr *n = te_compile("mysum(5, 6)", vars, 1, 0);
}

void loop() {}

And this is the compiler error message:

sketch_mar31a:9:56: error: invalid conversion from 'double (*)(double, double)' to 'const void*' [-fpermissive]
 te_variable vars[] = { {"mysum", my_sum, TE_FUNCTION2} };
                                                        ^
exit status 1
invalid conversion from 'double (*)(double, double)' to 'const void*' [-fpermissive]

Any ideas? Thanks in advance.

pbdev001 commented 8 months ago

I have also tried to match the function declaration in the initialization:

double x, y;

te_variable vars[] = { {"mysum", my_sum(x,y), TE_FUNCTION2} };

and got this: error: cannot convert 'double' to 'const void*' in initialization

codeplea commented 8 months ago

Try to cast it like this:

te_variable vars[] = { {"mysum", (const void*) my_sum, TE_FUNCTION2} };

pbdev001 commented 8 months ago

Great, that solved the compile error!

I´m willing to use custom functions to have a few basic logical functions (And, Or, Xor) and maybe combine them for more complex operations.

Besides the logical functions, I´ve kept a simple sum as per the example as referenca, but none of them is passing the error control at te_compile(). This is my code:

#include "tinyexpr.h"

int my_Sum(int a, int b) { return a + b; }
bool my_Or(bool a, bool b) { return a || b; }
bool my_And(bool a, bool b) { return a && b; }
bool my_Xor(bool a, bool b) { return a ^ b; }

int c, d;
bool x, y;

te_variable vars[] = { 
    { "c", &c }, { "d", &d },
    { "x", &x }, { "y", &y },
    {"mySum", (const void*) my_Sum, TE_FUNCTION2},
    {"myOr",  (const void*) my_Or, TE_FUNCTION2},
    {"myAnd",  (const void*) my_And, TE_FUNCTION2},
    {"myXor",  (const void*) my_Xor, TE_FUNCTION2},
};

void setup() {

  delay(500);
  Serial.begin(115200);       // prepara puerto serial
  Serial.flush();
  delay(1200);
  Serial.println();
  Serial.println("-----TinyExpr-----");

  c = 5; d = 4;
  x = true; y = true;

  char* expr[5] = { "mySum(c,d)", "myOr(x,y)", "myAnd(x,y)", "myXor(x,y)", "myXor(myOr(x,y),myAnd(x,y))" };
  int err;

  Serial.printf("Evaluating for c=%d, d=%d, x=%d and y=%d\n",c,d,x,y);
  for (int i=0; i<5; i++) {
    te_expr *n = te_compile(expr[i], vars, 2, &err);
    if (n) {
      int res = te_eval(n);
      Serial.printf("Expr = %s  - Result = %d\n",expr[i],res);
      te_free(n);
    } else {  
      Serial.printf("** Error @ pos = %d in Expr = %s\n",err,expr[i]);
    }
  }
}

void loop() {}

And this is the output:

-----TinyExpr-----
Evaluating for c=5, d=4, x=1 and y=1
** Error @ pos = 5 in Expr = mySum(c,d)
** Error @ pos = 4 in Expr = myOr(x,y)
** Error @ pos = 5 in Expr = myAnd(x,y)
** Error @ pos = 5 in Expr = myXor(x,y)
** Error @ pos = 5 in Expr = myXor(myOr(x,y),myAnd(x,y))

Any suggestions? Thanks!

codeplea commented 8 months ago

When you pass 2 to te_compile, it is only seeing your first two variables, e.g. c and d. You should replace the 2 with 8 and I think it will pass te_compile.

Your next issue is that only double is supported. I suspect it will crash at runtime unless you change all your variables and functions to use double only. You can convert the double arguments to ints in your function, of course. But the inputs and outputs to the function should be double.

Finally, hopefully you already know this, but ^ is a bit-wise operation, where-as all your others are logical operations. I suppose it doesn't matter if you're doing it on one-bit integers, but it seems like the sort of thing that will get you into trouble. Logical xor is rarely used, but it's probably more something like (!a) != (!b), assuming a and b are integers.

Also, there is a fork of tinyexpr that supports boolean logic and other things if you don't want to do this all yourself.

pbdev001 commented 8 months ago

Thanks for detailed feedback. I´ve corrected everything and managed to compile and run a set of basic logical operators. My bad about the bitwise XOR 👍

I´ve seen tinyExpr++ but it uses C++17 which I haven´t been able to fully setup in my (somewhat older) IDE.

Here´s the working code and the output:

#include "tinyexpr.h"

double my_Sum(double a, double b) { return a + b; }
double my_Or(double a, double b) { return a || b; }
double my_And(double a,double b) { return a && b; }
double my_Xor(double a, double b) { return (!a) != (!b); }
double my_And3(double a,double b, double j) { return a && b && j; }

double c, d, x, y, z;

te_variable vars[] = { 
    { "c", &c }, { "d", &d },
    { "x", &x }, { "y", &y }, {"z", &z },
    {"mySum", (const void*) my_Sum, TE_FUNCTION2},
    {"myOr",  (const void*) my_Or, TE_FUNCTION2},
    {"myAnd",  (const void*) my_And, TE_FUNCTION2},
    {"myXor",  (const void*) my_Xor, TE_FUNCTION2},
    {"myAnd3",  (const void*) my_And3, TE_FUNCTION3},    
};

void setup() {

  delay(500);
  Serial.begin(115200);       // prepara puerto serial
  Serial.flush();  delay(1200);
  Serial.println();
  Serial.println("-----TinyExpr-----");

  c = 5; d = 4; x = 1; y = 1; z = 0;

  char* expr[5] = { "mySum(c,d)", "myOr(x,y)", "myAnd(x,y)", "myXor(x,y)", "myAnd3(x,y,z)" };
  int err;

  Serial.printf("Evaluating for c = %0.f, d = %0.f, x = %0.f, y = %0.f and z = %0.f \n",c,d,x,y,z);
  for (int i=0; i<5; i++) {
    te_expr *n = te_compile(expr[i], vars, 10, &err);
    if (n) {
      int res = te_eval(n);
      Serial.printf("Expr = %s  - Result = %d\n",expr[i],res);
      te_free(n);
    } else {  
      Serial.printf("** Error @ pos = %d in Expr = %s\n",err,expr[i]);
    }
  }
}

void loop() {}

Output:

-----TinyExpr-----
Evaluating for c = 5, d = 4, x = 1, y = 1 and z = 0 
Expr = mySum(c,d)  - Result = 9
Expr = myOr(x,y)  - Result = 1
Expr = myAnd(x,y)  - Result = 1
Expr = myXor(x,y)  - Result = 0
Expr = myAnd3(x,y,z)  - Result = 0

Thanks again!

codeplea commented 8 months ago

I'm glad it's working for you!