【问题标题】:Passing operator as a parameter in C99在 C99 中将运算符作为参数传递
【发布时间】:2016-03-17 00:43:11
【问题描述】:

我想在 C99 中将运算符作为参数传递。 我的解决方案是这样的:

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);
}

还有其他方法吗?我不想为每个运算符编写一个函数。

它可能看起来像这样:

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

【问题讨论】:

  • “还有其他方法吗?” - 当然:使用不同的语言进行动态代码生成。
  • 不,这不可能直接。你想做什么?有时,宏可以解决问题。
  • perform(add, 3, 2) 怎么不酷?再好不过了,而且不涉及宏。

标签: c operators parameter-passing c99


【解决方案1】:

你可以使用 switch/case,例如:

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

但是您仍然需要为每个运算符编写一些代码;在 C 语言中,您不会免费获得任何东西。

【讨论】:

    【解决方案2】:

    您可以使用X Macros。通过在可重新定义的宏中定义包含重复值表的单个宏,您只需为当前任务重新定义内部宏并插入单个宏来处理整个集合。

    这里有一个简洁的方法,以单操作数浮点内置函数为例。其他类型的过程类似。

    //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;
    }
    

    你也可以继续使用它来做其他事情

    ///////////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
    

    编辑:注意宏中的 $ 是依赖于实现的,你可以随便调用它

    Edit2:这是一个使用多个参数来执行算术运算符的示例。这个使用计算的 goto 而不是 switch,以防你的编译器处理一个比另一个更好。

    #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 $
    }
    

    【讨论】:

    • 内置函数的好解决方案。但是,如何将它用于基本的算术运算符?我错过了什么吗?
    • @MartinZabel - 见最后一个例子#define $(x,op) x: return a op c;
    • 第二次编辑:不应该是&amp;&amp;x - &amp;&amp;SUB&amp;&amp;SUB + array[b]吗?
    • @MartinZabel 取决于 char 是有符号还是无符号。如果是签名的,你要选一个靠近中间的(ADD);如果它是无符号的,你会选择第一个(SUB)......这取决于架构以及地址的实际范围和编译器(clang为此方法产生近乎最佳的代码)。
    猜你喜欢
    • 1970-01-01
    • 2017-11-18
    • 2018-03-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    相关资源
    最近更新 更多