Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a C math operator (+-*/%) into a function result=mathfunc(x,+,y);?

Tags:

c

I'm writing some math routines in a C program for multi-precision integers and I want to be able to easily write expressions, but handle the math with my own function. So I want some way that lets me do this:

MPI x=mpi(5),y=mpi(6),z;
z=mpimath(x,+,y);

Is this possible in C by encoding the character somehow? I know there's no operator overloading so it has to be a function call, and the +-sign cannot be part of the function name.

like image 633
luser droog Avatar asked Apr 30 '15 06:04

luser droog


1 Answers

Yes, it is possible.

You can interface to your function using a macro. A macro gives you extra powers over the physical appearance of the code. In particular, you can stringify an argument. And you can pass any complete C token as an argument to a macro.

So do something like this:

#define mpimath(X,F,Y) (mpimath)(X,#F,Y)  /* stringify F */

Then define your function to accept a char * parameter. This will result in the string "+" for the argument +. By using the same name for the macro, calls to the function are intercepted by this macro so a call like this:

mpimath(p,*,r)   /* macro invocation */

is expanded to

(mpimath)(p,"*",r)   /* function call */

. The parens around the function name are not strictly necessary here, since a macro is not allowed to be recursive, it will expand to the right thing. But wrapping parens is also the way to explicitly call the function bypassing any macro definitions, so I find it helps self-document the code to add them here.

You can use something like char *ops="+-*/"; int op=strstr(ops, f)-ops; to map the string to a small integer (*) which can then be used to index a function-table. Or you can dereference the string to get a char which you can use in a switch.

MPI mpimath(MPI x, char *f, MPI y){
    switch(*f){
    case '+': //...
              break;
    case '-': //...
              break;
    //etc.
}

Or you can move the dereferencing back into the macro. It looks a little weird in the macro. But I think it makes the function look nicer.

#define mpimath(X,F,Y) (mpimath)(X,*#F,Y) /* stringify and deref F */

MPI mpimath(MPI x, char f, MPI y){
    switch(f){
    case '+':
    //etc.
    }
}

Edit: Some details about the actual code that gave rise to this share-your-knowledge q/a pair.

The motivation for the passing the operator as a char arose indirectly, but never in the simplified form shown in the question. The current use of macro looks like this for the + function:

 BIN_MATH_FUNC(+,AV(z)[i],AV(a)[i],AV(w)[i],plusover,plusdomainI,plusdomainD)

and the macro also directly handles native types like int and double. So the framework that the mpi function needed to fit was already set up to distinguish operations by their actual C operator (as a macro argument). So the real situation didn't allow for the use of enum symbols.


(*) This is not, to put it politely, a good way to do it. The code relies upon the compiler to condense the two strings into one location in the compiled program's string-table. This is a typical (one might even say obvious) optimization for the compiler to do, but is it not guaranteed by any standard. It would be better (though more verbose) to assign the string literal to a char * variable and use the variable in both places. You can (and probably should) test the result of strstr for `NULL before doing anything with the result.

like image 123
luser droog Avatar answered Nov 15 '22 03:11

luser droog