mejem
mejem

Reputation: 63

Passing operator as a parameter in C99

I want to pass an operator as a parameter in C99. My solution is this:

int add(int l, int r)
{
    return l + r;
}

int sub(int l, int r)
{
    return l - r;
}

// ... long list of operator functions

int perform(int (*f)(int, int), int left, int right)
{
    return f(left, right);
}

int main(void)
{
    int a = perform(&add, 3, 2);
}

Is there some other way to do it? I don't want to write a function for every operator.

It could look like this:

int a = perform(something_cool_here, 3, 2);

Upvotes: 3

Views: 1887

Answers (2)

technosaurus
technosaurus

Reputation: 7802

You can use X Macros. By defining a single macro that contains a table of repeated values in a redefinable macro, you can just redefine the internal macro for the current task and insert a single macro to handle the whole set.

Here is a compact way to do it with single operand floating point builtins as an example. The process is similar for other types.

//add name of each function you want to use here:
#define UNARYFPBUILTINS \
    $(acos)  $(acosh)  $(asin)  $(asinh)  $(atan)   $(atanh)  $(cbrt)  $(ceil)  \
    $(cos)   $(erf)    $(erfc)  $(exp)    $(exp10)  $(exp2)   $(expm1) $(fabs)  \
    $(floor) $(gamma)  $(j0)    $(j1)     $(lgamma) $(log)    $(log10) $(log1p) \
    $(log2)  $(logb)   $(pow10) $(round)  $(signbit)          $(significand)  \
    $(sin)   $(sqrt)   $(tan)   $(tgamma) $(trunc)  $(y0)     $(y1)

//now define the $(x) macro for our current use case - defining enums
#define $(x) UFPOP_##x,
enum ufp_enum{ UNARYFPBUILTINS };
#undef $  //undefine the $(x) macro so we can reuse it

//feel free to remove the __builtin_## ... its just an optimization
double op(enum ufp_enum op, double f){
  switch(op){  //now we can use the same macros for our cases
#define $(x) case UFPOP_##x : f = __builtin_##x(f);break;
   UNARYFPBUILTINS
#undef $
  }
  return f;
}

You can continue using it for other stuff as well

///////////EXTRA STUFF/////////
//unused - may be good mapping the enums to strings
//#define $(x) #x,
//const char * ufp_strings{ UNARYFPBUILTINS };
//#undef $

//this uses float instead of double, so adds the ##f to each function
float opf(enum ufp_enum op, float f){
  switch(op){
#define $(x) case UFPOP_##x : f = __builtin_##x##f(f);break;
    UNARYFPBUILTINS
#undef $
  }
  return f;
}

//you could do the same thing for long double here

Edit: Note that $ in macros is implementation dependent, You can call it whatever

Edit2: Here is an example with multiple parameters to do arithmetic operators. This one uses computed gotos instead of a switch in case your compiler handles one better than the other.

#define IOPS $(SUB,-) $(MUL,*) $(DIV,/) $(MOD,%) $(ADD,+) $(AND,&) $(OR,|) \
  $(XOR,^) $(SR,>>) $(SL,<<)

enum iops_enum {
#define $(x,op)   IOPSENUM_##x,
  IOPS
  IOPSENUM_COUNT
#undef $
};
int opi(int a, enum iops_enum b, int c){
  static const char array[] = { //you may get better results with short or int
#define $(x,op)   &&x - &&ADD,
    IOPS
#undef $
  };
  if (b >= IOPSENUM_COUNT) return a;
  goto *(&&ADD + array[b]);
  //else should give a warning here.
#define $(x,op)   x: return a op c;
  IOPS
#undef $
}

Upvotes: 4

Dave the Sax
Dave the Sax

Reputation: 328

You could use switch/case, for example:

int perform(char op,int a,int b)
{
    switch (op)
    {
    case '+': return a+b;
    case '-': return a-b;
    default: return 0;
    }
}

But you would still have to write some code for each operator; you don't get anything for free in C.

Upvotes: 5

Related Questions