2015-11-18 3 views
0

Вчера вечером я написал следующий фрагмент кода для выполнения некоторой арифметической операции (сумма, умножение, вычитание, деление).Обобщение указателей функций

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; 

данные - это структура, которая содержит либо значения базовых типов, либо комплексные значения. Сложными значениями являются типы и структуры массивов. О каком, на данный момент, мне все равно.

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

Базовые значения atm содержат только целые числа, поплавки и булевы.

Но мне было интересно, смогу ли я сделать его более компактным и эффективным. Например, я объявил два указателя на функцию, но я использую только один из них. Есть ли способ обобщить указатель на функцию?

Я знаю, что я должен объявлять типы ввода и тип возвращаемого значения, но в этом случае было бы здорово отложить такую ​​специализацию функций, чтобы иметь только один указатель. Возможно ли это, или я должен изменить способ решения проблемы?

Любое конструктивное предложение хорошо принимается :)

+0

Предоставить [mcve]. Что такое 'данные'? – Olaf

+0

В первой части вы можете выбрать 'struct {int (* funIntPtr) (int, int); float (* funFloatPtr) (float, float); } ', и оставьте проверку типа ко второй части. –

+0

У вас есть проблемы с копированием/вставкой в ​​'case DIVIDE' (это то же самое, что и' case MULTIPLY'), а также вы должны делить на 0. –

ответ

0

В вашем примере я не вижу функции с плавающей точкой используется:

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

И именно поэтому вы не можете использовать один указатель на функцию: существует огромная разница в том, как функции float (double) и integer передаются функции и как результат возвращается. Конечно, вы можете сделать тяжелый кастинг, просто чтобы сохранить указатель на функцию, но это будет запутывать код, поэтому я советую против этого.

+0

Примечание: перед вызовом функции вы должны проверить, что оба операнда, которые читает ваш синтаксический анализатор, имеют правильный тип, или вы должны их преобразовать в этот тип; например, e2 имеет значение int, а e1 имеет значение float, требующее вызова 'funFloatPtr (e1.bf, e2.bi);' –

+0

проверка типа происходит до вызова этой функции, так что пока не предусмотрено принуждение, они предполагается, что тот же тип .. – LMG

+0

, так что вы говорите: «Конечно, вы можете делать тяжелый кастинг, просто чтобы сохранить указатель на функцию, но это будет запутывать код, поэтому я советую против этого». Это лучше иметь читаемый а не компактный? – LMG

0

Вы можете использовать folowing структуру:

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

Кроме того, вы можете комбинировать arity и operators карты.

Еще одна вещь заключается в том, что объявление статического массива для аргументов может потребовать знания максимального значения из arity значений. Эта структура позволит вам реализовать операторы any-arity без перезаписи кода.

Кроме того, вместо double вы можете использовать любую структуру обобщенного значения, которую вы хотите.

Смежные вопросы