Арифметические операции с указателями void * на числовые данные - PullRequest
3 голосов
/ 24 февраля 2010

Я работаю над небольшим парсером и «решателем уравнений» в C, часть этого процесса - выполнять арифметические операции над токенами Каждый токен содержит void * указатель на числовые данные и enum, который определяет тип данных.

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

  1. тип чека
  2. литье
  3. сделать операцию
  4. создать новый токен из результата

а

Token* _CreateTokenByAddition(Token* arg1, Token* arg2){
    Token *tokenResult;
    if ((arg1->_type == tk_IntData) && (arg2->_type == tk_IntData)){

        int* intResult = malloc(sizeof(int));
        *intResult = *(int*)arg1->_data + *(int*)arg2->_data;

        tokenResult = CreateTokenFromValue(intResult, tk_IntData);
    }else
    if ((arg1->_type == tk_IntData) && (arg2->_type == tk_FloatData)){

        float* intResult = malloc(sizeof(float));
        *intResult = *(int*)arg1->_data + *(float*)arg2->_data;
        tokenResult = CreateTokenFromValue(intResult, tk_FloatData);
    }else
    if ((arg1->_type == tk_FloatData) && (arg2->_type == tk_IntData)){

        float* intResult = malloc(sizeof(float));
        *intResult = *(float*)arg1->_data + *(int*)arg2->_data;
        tokenResult = CreateTokenFromValue(intResult, tk_FloatData);
    }
    else
    if ((arg1->_type == tk_FloatData) && (arg2->_type == tk_FloatData)){

        float* intResult = malloc(sizeof(float));
        *intResult = *(float*)arg1->_data + *(float*)arg2->_data;
        tokenResult = CreateTokenFromValue(intResult, tk_FloatData);
    }
    return tokenResult;
}

У меня почти идентичные функции для -, *, /. И мне, вероятно, нужно будет создать больше.

Вопросы: Как я могу создать одну универсальную функцию, которая будет поддерживать все простые операции, такие как + - * /? Я не хочу помещать эту функцию в макрос, а затем дублировать ее 4 раза, заменяя математический операнд. В любом случае, как я могу упростить проверки типов данных и приведение типов из пустых указателей?

В любом случае, я могу сделать этот код лучше?

Предположение: у меня нет никаких нечисловых типов данных (например, строк)

Спасибо

Круто, большое спасибо за эти ответы, я понимаю, что вы подразумеваете под указателями на функции. Я собираюсь подумать об этом и использовать один из этих методов. Спасибо

Ответы [ 4 ]

4 голосов
/ 24 февраля 2010

Краткий ответ: c не предоставляет никакой синтаксической помощи для этого.

Хорошие новости: Вы можете поддерживать полиморфизм в c, используя указатели на функции. Есть много вопросов, объясняющих, как сделать , который уже присутствует в переполнении стека. Через минуту я отредактирую ссылку или две ...

Не нравится ответ, который я нашел для этого использования, поэтому здесь идет.

Для каждой операции напишите набор функций, которые принимают все допустимые комбинации типов. Каждый делает ровно одну комбинацию, так что это легко. Затем создайте таблицу указателей на функции, как это

typedef Token* (*mathfuncptr)(void *, void *);

Token* _Add_IntInt(void *, void *);
Token* _Add_IntFloat(void *, void *);
Token* _Add_FloatInt(void *, void *);
/* ... */

mathfuncptr Add_table[AddTypeCount][AddTypeCount] = {
  {_Add_IntInt,   _Add_IntFloat},
  {_Add_FloatInt, _Add_FloatFloat}
};
mathfuncptr Mul_table[MultTypeCount][MultTypeCount] = { /* ... */

А теперь ваша общая функция сложения определяет два типа, которые имеет , и вызывает нужную функцию путем индексации в таблице.

3 голосов
/ 24 февраля 2010

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

typedef int (*bin_op)(int, int);
typedef float (*bin_fop)(float, float);

Token* _CreateTokenByOp(Token* arg1, Token* arg2, bin_op op, bin_fop, fop) {
   ...
   *intResult = op(*(int*)arg1->_data, *(int*)arg2->_data);
   ...
}

int add(int a, int b) {  return a+b;  }
float addf(float a, float b) {  return a+b;  }
...

Token* _CreateTokenByAddition(Token* arg1, Token* arg2) {
   return _CreateTokenByOp(arg1, arg2, &add, &addf);
}

При этом C обычно не очень хорош в создании универсальных функций, и использование указателей на функции может быстро привести к довольно непонятному коду. Использование такого языка, как C ++, который поддерживает объектную ориентацию и подобные концепции, может сделать вашу жизнь намного проще.

1 голос
/ 24 февраля 2010
  • Вы можете использовать switch вместо нескольких if-else.

  • Таблица указателей функций, проиндексированных перечислением, соответствующим типам.

например:

typedef enum type_info_ { INT = 0, FLOAT, ... , UNKNOWN } TI;

   typdef void *(*generic_add_t)(void const*, void const*);

   void *float_add(void const* l, void const* r) {
        float *result = malloc(sizeof *result);
        *result = *((float const *)l) + *((float const *)r);
        return result;
   }




generic_add_t fp_table[] = { float_add, ... };

и используйте его как:

  TI curr_type = UNKNOWN;
  // ...
  fp[ curr_type ];

И если вам лень выкатывать все сумматоры, определитемакрос:

#define MAKE_ADDER(type) \
void *type##_add(void const* l, void const* r) { \
        type *result = malloc(sizeof *result); \
        *result = *((type const *)l) + *((type const *)r); \
        return result; \
   } 

и использовать его как:

MAKE_ADDER(float)
MAKE_ADDER(int)

...
0 голосов
/ 24 февраля 2010

Вы можете легко сделать это с помощью указателя на функцию и оператора switch.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...