Reputation: 63
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
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
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