0

I have some C code where I need to do some calculations with an array of data. The data can be either INT or DOUBLE. In order to deal with the different data types, I was thinking of using an if / else statement and define the pointer holding the data inside that statement:

/* put values into M, depending on data type*/
if (data_type == 2)
{
    double *M;  
    M = somefunction(DOUBLE);
} else {
    unsigned int *M;
    M = somefunction(UINT16);
}

/* dummy code - usually I do some calculations on M which are data type independent */
for (i=0;i<(10);i++) {
    M[i]=0;
}

This leads to scoping problems because M is not defined outside the if / else construct:

 error: ‘M’ undeclared (first use in this function)

If I move the definition of M outside the if / else statement, the code will compile but M inside the if / else is a different M outside.

So I can circumvent the problem by defining two pointers, one double and one int and check everywhere in my code which type I'm dealing with:

double *Mdouble;  
unsigned int *Mint;
/* put values into M, depending on data type*/
if (data_type == 2)
{
    Mdouble = somefunction(DOUBLE);
} else {
    Mint = somefunction(UINT16);
}

/* dummy code - usually I do some calculations on M which are data type independent */
for (i=0;i<(10);i++) {
    if (data_type == 2) {
        Mdouble[i]=0;
    } else {
        Mint[i]=0;
    } 
}

So here's my question:

How can I solve this problem where M is a double or int, depending on my incoming data? Could I solve this with some kind of pointer to a pointer work around? I don't want to write duplicate code for each case.

EDIT could template functions or overloading of functions solve my problem? I'm flexible regarding a C / C++ specific solution

memyself
  • 11,907
  • 14
  • 61
  • 102
  • 2
    It looks like you're about to invent discriminated unions. – Kerrek SB Oct 22 '14 at 09:54
  • 1
    With C++ (removed tag), you may have `somefunction` a template function which may solve your issue. – Jarod42 Oct 22 '14 at 09:58
  • @Jarod42 how would I do that? – memyself Oct 22 '14 at 10:05
  • Is it C or C++? I remove the **c++** tag because you said it's C code in the question. Now you mentioned template. – Yu Hao Oct 22 '14 at 10:06
  • @YuHao nothing C / C++ specific in my code, so I'm flexible to use whatever mechanism is available in either language to solve my problem. – memyself Oct 22 '14 at 10:08
  • @memyself the problem is that C has no run-time support for types, C++ has. – AndersK Oct 22 '14 at 10:10
  • @Claptrap Do not confuse templates with rtti. – user877329 Oct 22 '14 at 10:13
  • @user877329 i am not, his problem could be solved with rtti as well – AndersK Oct 22 '14 at 10:17
  • @Claptrap Not without a major rewrite (which might be the correct solution); RTTI only works on polymorphic types (class types with at least one virtual function). – James Kanze Oct 22 '14 at 10:32
  • @JamesKanze i was just mentioning that option in light of the discussion of how to solve his problem. I believe the amount of work it would take seems besides the point - he didn't ask for a quick fix as far I could tell. Inheritance has its own problems but in some cases it can be useful. – AndersK Oct 22 '14 at 12:33
  • @Claptrap Inheritance is often useful; I make a lot of use of it. It just doesn't apply to `int` and `double`. – James Kanze Oct 23 '14 at 08:44
  • @JamesKanze hw can create his own int and double derived from a common base class to solve his problem. – AndersK Oct 24 '14 at 04:08

5 Answers5

3

You're going to have to write duplicate code.

At the base level, the required machine code for adding two numbers is different for integers vs floating-point numbers.

This difference is "hidden" since data is typed, so the compiler always knows the type of each operand and can generate the proper code.

If you want to move that information until run-time, the compiler can no longer do its thing, so you're going to have to do it instead and make sure your code takes the proper path.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • @memyself with templates you can separate the algorithm from the data type, probably best if you google after c++ template - cause it is a lot to explain. – AndersK Oct 22 '14 at 10:08
  • @memyself There are no templates in C. You can use generics in C99 perhaps, I'm not familiar with them myself. :( – unwind Oct 22 '14 at 10:09
  • @unwind It's not too difficult to simulate what templates do using macros in C. You don't get automatic instantiation, nor type deduction, but long before C++ had templates, I was using a vector and a shared pointer which were based on macros. – James Kanze Oct 22 '14 at 10:34
2

You can try to use an C union:

#include <string.h>
#include <stdio.h>

int main(int argc, char** argv) {
  union MyUnion {
    int I;
    double D;
  };
  union MyUnion M[10];
  // Initializing the array to 0 (data type independent)
  memset(M, 0, 10*sizeof(MyUnion));
  M[0].I = 1;
  M[1].D = 1.5;
  if (argc==1) { // It should be "if (data_type==2) {"
                 // but I wanted an example that compiles easily
    printf("%i\n", M[0].I); //somefunction(M[0].I);
  } else {
    printf("%lf\n", M[1].D); //somefunction(M[1].D);
  }
}
Brahim
  • 808
  • 1
  • 8
  • 17
  • could you please elaborate on that example and maybe connect it to my example above? I don't see the connection right now. thanks! – memyself Oct 22 '14 at 10:50
  • I have just edited the code to add some comments. My point is that you can use unions to make the same data interpreted differently. But I think it won't solve your problem if you need to make arithmetic operation independent of the data type. – Brahim Oct 22 '14 at 13:01
1

In C you will have to use a macro

int8_t* ptr=...;
while(n)
    {
    switch(data_type)
        {
        case TYPE_DOUBLE:
            A_MACRO_THAT_DEFINES_YOUR_OPERATION((double*)ptr);
            ptr+=sizeof(double);
            break;
        case TYPE_INT:
            A_MACRO_THAT_DEFINES_YOUR_OPERATION((int*)ptr);
            ptr+=sizeof(int);
            break;
        }
    --n;
    }

This solution is slow since it needs to test the data type for each element. You can instead write the entire loop as a macro. This will be faster but harder to read.

If you can use C++, use a template instead. Here is a solution using templates:

template<class T>
void doStuff(T* ptr_begin,T* end)
    {
    while(ptr_begin!=ptr_end)
        {
   //   Do stuff
        ++ptr_begin;
        }
    }

void doStuffWrapper(void* ptr_begin,void* ptr_end,uint32_t type)
    {
    switch(type)
        {
        case TYPE_DOUBLE:
            doStuff((double*)ptr_begin,(double*)ptr_end);
            break;
        case TYPE_INT:
            doStuff((int*)ptr_begin,(int*)ptr_end);
            break;
        }
    }

As a side note: I prefer switch-case over if-else in this case since it is easier to maintain, and may produce faster code.

user877329
  • 6,717
  • 8
  • 46
  • 88
0

You can use a macro, e.g.:

#define CALCULATIONS(M) do { \
    for (i=0;i<(10);i++) \
        (M)[i]=0; \
    } while (0)

and then in your other code:

if ( data_type == 2 )
{
    double *M = whatever;
    CALCULATIONS(M);
}

See here if you are unfamiliar with the do...while(0) technique

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • why is the `while (0)` part necessary? is there a `{` missing in line 2? – memyself Oct 22 '14 at 10:14
  • @MattMcNabb The `do...while(0)` is a standard idiom to make the macro look like a single statement. Without it, things like `if CALCULATIONS(M); else ...` may end up being illegal, or worse, doing something different than expected. – James Kanze Oct 22 '14 at 10:36
  • @JamesKanze you mis-pinged – M.M Oct 22 '14 at 11:02
  • Sorry. Automatic completion, and I just hit the tab to accept it without looking. My response is obviously for @memyself. – James Kanze Oct 22 '14 at 11:15
0

It sounds like you want a templatized function.

But I think the question comes down to, can you put all your work in the function?

template <typename T>
T* somefunction(T data){
    //start old somefunction code
    T* result = new T[10]{data, data, data, data, data, data, data, data, data, data};
    //end old somefunction code

    //begin "calculations on M which are data type independent"
    for(int i = 0; i < 10; i++){
        M[i] = 0;
    }
    return M;
}

So with the above code you could do either double* Mdouble = somefunction(13.13) or short* Mshort = somefunction(13);

The best case scenario is in which M was really a throw away array anyway, in which case you could even allocate the data in the template function's stack frame and avoid using new.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288