【问题标题】:generalize function pointers泛化函数指针
【发布时间】:2015-11-18 18:33:40
【问题描述】:

昨天晚上我编写了以下代码来执行一些算术运算(求和、乘法、减法、除法)。

 data operation(int oper, data e1, data e2){
   data res;
   basic b;
   //pointer to a funcion that takes two int input and return an int
   int (*funIntPtr)(int,int);
   float (*funFloatPtr)(float,float);

   funIntPtr = NULL;
   funFloatPtr= NULL;
   //I look for the function to be called
     switch (oper) {
       case MINUS:
         //*functionPtr = &subInt;
         switch (e1.b.type) {
           case basic_int_value:funIntPtr = subInt;break;
           case basic_float_value:funFloatPtr = subFloat;break;
           case basic_boolean_value:
             yyerror("boolean arithmetic operation are NOT_ALLOWED");
             exit(NOT_ALLOWED);
             break;
         }
         break;
       case PLUS :
         switch (e1.b.type) {
           case basic_int_value:funIntPtr = addInt;break;
           case basic_float_value:funFloatPtr = addFloat;break;
           case basic_boolean_value:
             yyerror("boolean arithmetic operation are NOT_ALLOWED");
             exit(NOT_ALLOWED);
             break;
         }
         break;
       case MULTIPLY:
         switch (e1.b.type) {
           case basic_int_value:funIntPtr = mulInt;break;
           case basic_float_value:funFloatPtr = mulFloat;break;
           case basic_boolean_value:
             yyerror("boolean arithmetic operation are NOT_ALLOWED");
             exit(NOT_ALLOWED);
             break;
         }
         break;
       case DIVIDE :
         switch (e1.b.type) {
           case basic_int_value:funIntPtr = divInt;break;
           case basic_float_value:funFloatPtr = divFloat;break;
           case basic_boolean_value:
             yyerror("boolean arithmetic operation are NOT_ALLOWED");
             exit(NOT_ALLOWED);
             break;
         }
         break;
       default:
         yyerror("what now?");
         exit(BUGGY_THE_CLOWN);
         break;
      }
    //look for values to be used
    if( funIntPtr == NULL && funFloatPtr == NULL){
      yyerror("no function found for the specified operation..");
      exit(BUGGY_THE_CLOWN);
    }
    res.type = basic_dataType;
    res.b.type = e1.b.type;//inherithed
    switch (e1.b.type) {
      case basic_int_value:
        {
          res.b.i = funIntPtr(e1.b.i, e2.b.i);
        }
        break;
      case basic_float_value:
        {
          res.b.f = funFloatPtr(e1.b.f, e2.b.f);
        }
        break;
      case basic_boolean_value:
      default:
        yyerror("no data found for the specified operation..");
        exit(BUGGY_THE_CLOWN);
    }
   return res;
 }

在第一部分我找到要使用的函数,在第二部分我收集要使用的输入数据。它工作正常,可以达到目的。

typedef struct data{
    dataType type;
    union{
      complex c;
      basic b;
    };
}data;

data 是一个包含基本类型值或复杂值的结构。复数值是数组类型和结构。目前,我不在乎。

typedef struct basic{
  basicType type;
  union{
     int i;
     float f;
     bool b;
  };
}basic;

基本值 atm 仅包含整数、浮点数和布尔值。

但我想知道我是否可以使它更紧凑和更高效。 例如,我声明了两个函数指针,但我只使用其中一个。有没有办法泛化函数指针?

我知道我必须声明输入类型和返回类型,但在这种情况下,最好推迟这种函数特化以便只有一个指针。 是否有可能或者我应该改变解决问题的方式?

任何建设性的建议都会被接受:)

【问题讨论】:

  • 提供minimal reproducible exampledata 是什么?
  • 在第一部分,您可以选择struct { int (*funIntPtr)(int,int); float (*funFloatPtr)(float,float); },然后将类型检查留给第二部分。
  • 您在case DIVIDE 中存在复制/粘贴问题(与case MULTIPLY 相同),并且您应该捕获除以0。
  • @WeatherVane 类型检查发生在此函数调用之前,我假设我不能除以零,周围也存在 int 除法问题,但对于示例来说这很好。问题现已根据要求更新,提供更多信息。
  • @LMG 我看到你编辑了case DIVIDE,但像这样的基本错误让我对这个问题失去信心。

标签: c pointers function-pointers


【解决方案1】:

在您的示例中,我没有看到使用的 float 函数:

  case basic_float_value:
    {
      res.b.i = funIntPtr(e1.b.i, e2.b.i);
    }

我希望是这样的:

  case basic_float_value:
    {
      res.b.f = funFloatPtr(e1.b.f, e2.b.f);
    }

这正是您不能使用单个函数指针的原因:浮点(双精度)和整数参数传递给函数的方式以及返回结果的方式存在巨大差异。当然你可以做大量的转换,只是为了保存一个函数指针,但这会混淆代码,所以我建议不要这样做。

【讨论】:

  • 注意:在调用函数之前,您必须检查您的解析器读取的两个操作数的类型是否正确,或者您必须将它们转换/转换为该类型;例如,e2 有一个 int 值,而 e1 有一个 float 值需要调用 funFloatPtr(e1.b.f, e2.b.i);
  • 类型检查发生在此函数调用之前,因此由于尚未提供强制,因此假定它们属于同一类型..
  • 所以你说“当然你可以做大量的转换,只是为了保存一个函数指针,但这会混淆代码,所以我建议不要这样做。”,最好有一个可读的代码而不是比紧凑型的?
  • 是的,总是。可读的代码是可维护的;紧凑的代码并不总是如此。可读的紧凑代码当然是最好的。
【解决方案2】:

您可以使用以下结构:

/* Operations enum */
enum operation {
    ADD, SUB, DIV, MUL, OP_COUNT
};

/* Operation arity */
static const int arity[OP_COUNT] = {
    [ADD] = 2, [SUB] = 2, [DIV] = 2, [MUL] = 2
};

/* Operation function pointer */
typedef double (*Operator)(double* arguments);

/* Map operation to operator */
Operator operators[OP_COUNT] = {
    [ADD] = add, [SUB] = sub, [DIV] = div, [MUL] = mul
};

double add(double *arguments) { return arguments[0] + arguments[1]; }
/* and so on for other operators */

另外,您可能想要组合 arityoperators 地图。

还有一件事是,为参数声明一个静态数组可能需要知道arity 值中的最大值。这种结构可以让您实现任意数量的运算符,而无需大量重写代码。

另外,除了double,您还可以使用任何您想要的通用值结构。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多