tags:

views:

79

answers:

4

Hi I am working on small parser and "equation solver" in C, part of this process is to do arithmetical operations on tokens. Each token contains void* pointer to numerical data, and enum, which defines type of data.

This is example of the function, which creates new token by adding two others tokens. In order to do so, I need to

  1. check type
  2. cast
  3. do operation
  4. create new token from result

a

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

I have almost identical functions for -, *, /. And I will probably need to create more.

Questions are: how I can create one generic function which will support all simple operations such as +-*/ ? I dont want to put this function in macro and then duplicate it 4 times by replacing mathematical operand. Any way how I can simplify data type checks and casts from void pointers?

Any way I can make this code, better ?

Assumption: I dont have any non numerical data types (eg strings)

Thanks

Cool, thanks a lot for these replies, I see what you mean by function pointers. I am going to think about it and use one of these methods. Thanks

+4  A: 

Short answer: c provides no syntaxtical help for that at all.

Good news: You can support polymorphism in c by using function pointers. There are many questions explaining how to do that already present on Stack Overflow. In a minute I'll edit in a link or two...

Not liking the answer that I found for this use, so here goes.

For each operation, write a set of functions that take all legal combinations of types. Each one does exactly one combination, so it is easy. Then construct a table of function pointers like this

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] = { /* ... */

And now your general add function determines the two types it has and calls the right function by indexing into the table.

dmckee
Trying for first answer advantage?
rpetrich
A: 

You can easily do it with a function pointer and a switch statement.

Frederic
+3  A: 

You could put the mathematical operation in a function pointer and then use a generic function that uses these function pointers:

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

That being said, C is not generally very good at creating generic functions and using function pointers can fast lead to quite obscure code. Using a language like C++ that supports object orientation and similar concepts could make your life a lot easier there.

sth
+1  A: 
  • You could use a switch instead of multiple if-else.

  • A table of function pointers indexed by an enum corresponding to the types.

e.g:

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, ... };

and use it as:

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

And if you're too lazy to roll out all adders, define a macro:

#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; \
   } 

and use it as:

MAKE_ADDER(float)
MAKE_ADDER(int)

...
dirkgently
Better than me with the constant correctness...
dmckee