5

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.

luser droog
  • 18,988
  • 3
  • 53
  • 105
  • There are overloaded operators like `-` and `/` in C. You can't add any overloads though, but that's not specific to operators but applies to functions in general. – Ulrich Eckhardt Apr 30 '15 at 06:14
  • 2
    You can pass the `+` as a character constant by enclosing it in single quotes, `z=mpimath(x,'+',y)` – user3386109 Apr 30 '15 at 06:30

2 Answers2

4

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.

luser droog
  • 18,988
  • 3
  • 53
  • 105
2

You can also use enums:

enum MathOps {PLUS, MINUS, MULTIPLY}; 

MPI mathfunc(MPI x, enum MathOps operation, MPI y)
{
    switch(operation){
    case PLUS: //...
              break;
    case MINUS: //...
              break;
    case MULTIPLAY: // .... 
              break;
    //etc.
}

Call:

mathfunc(x,PLUS,y)

The good thing about this approach is that you can easily see at the top of the file the operations that your function will support. The compiler will also complain if you add an enum but don't add it to the switch case which can be nice.

EDIT: Live example: http://ideone.com/GCJ9ro

Nixt
  • 144
  • 1
  • 9
  • Yes, that's a good way to do it. I also love enums. Somehow I never get that compiler warning, probably because I usually treat them as `int` and lose the typechecking. ... You could even paste the words with a macro `#define mathfunc(x.f.y) f##func(x,y)` and `MPI plusfunc (x, y)` etc. – luser droog Apr 30 '15 at 07:05
  • You are correct. I don't get that warning either. It was probably something I saw due to other syntax errors in the code before I fixed them. – Nixt Apr 30 '15 at 07:06
  • I think `splint` will give you (these) warnings. – luser droog Apr 30 '15 at 08:35