alrevuelta
alrevuelta

Reputation: 366

Variable input type in a function

I would like to perform different operations with vectors, for example, adding them, but I don't know the vector type on compile time (int, float, double). My requirement is that the type that is used under the hood, is the specified one, because it doesn't take the same time to multiply two int than two double. One option would be to have a function for each type, but I was exploring other approaches, like trying to reuse the same function. Since c is statically typed, this doesn't seem trivial to me.

My approach of reusing the same function is the following. Lets say for example, that I want to add two one dimensional vectors, which can be of any type:

int a[] = {1, 2, 3};
int b[] = {4, 5, 6};

Or it can also be:

float a[] = {1, 2, 3};
float b[] = {4, 5, 6};

So I would like to add both vectors, but with a generic function that doesn't know on compile time the types. My first idea was to use void* in the function declaration, and once inside the function, cast the vectors to the correct type. However, my implementation looks quite nasty.

void testVariableInput(void *a, void *b, void *out, int m, int type)
{
  int *aInt, *bInt, *outInt;
  float *aFloat, *bFloat, *outFloat;
  double *aDouble, *bDouble, *outDouble;

  if (type == 1)
  {
    aInt = (int*)a;
    bInt = (int*)b;
    outInt = (int*)out;

    for (int i = 0; i < m; i++)
    {
      outInt[i] = aInt[i] + bInt[i];
    }
  }
  else if (type == 2)
  {
    aFloat = (float*)a;
    bFloat = (float*)b;
    outFloat = (float*)out;

    for (int i = 0; i < m; i++)
    {
      outFloat[i] = aFloat[i] + bFloat[i];
    }
  }
  else if (type == 3)
  {
    aDouble = (double*)a;
    bDouble = (double*)b;
    outDouble = (double*)out;

    for (int i = 0; i < m; i++)
    {
      outDouble[i] = aDouble[i] + bDouble[i];
    }
  }

  if (type == 1)
  {
    out = (void*)outInt;
  }
  else if (type == 2)
  {
    out = (void*)outFloat;
  }
  else if (type == 3)
  {
    out = (void*)outDouble;
  }
}

And then just call the function. Not sure if the (void*) casting is needed though.

float a[] = {1, 2, 3};
float b[] = {4, 5, 6};
float c[] = {0, 0, 0};
testVariableInput((void*)a, (void*)b, (void*)c, 3, 2);

Note that the last parameter 2 is the type 1=int, 2=float, 3=double

I couldn't find any related example. Is there any design pattern for this? Or maybe there is a simpler way of archiving this?

Upvotes: 1

Views: 229

Answers (2)

KamilCuk
KamilCuk

Reputation: 141135

Or maybe there is a simpler way of achieving this?

I like function pointers. Here we can pass a function pointer that adds two elements. That way we can separate the logic of the function from the abstraction that handles the types.

#include <stdlib.h>
#include <stdio.h>

void add_floats(const void *a, const void *b, void *res){
   *(float*)res = *(const float*)a + *(const float*)b;
}

void add_ints(const void *a, const void *b, void *res) {
   *(int*)res = *(const int*)a + *(const int*)b;
}

void add_doubles(const void *a, const void *b, void *res) {
   *(double*)res = *(const double*)a + *(const double*)b;
}

void testVariableInput(const void *a, const void *b, void *out, 
            // arguments like for qsort
            size_t nmemb, size_t size, 
            // the function that adds two elements
            void (*add)(const void *a, const void *b, void *res)) {
    // we cast to all pointers to char to increment them properly
    const char *ca = a;
    const char *cb = b;
    char *cout = out;

    for (size_t i = 0; i < nmemb; ++i) {
       add(ca, cb, cout);

       ca += size;
       cb += size;
       cout += size;
    }
}

#define testVariableInput_g(a, b, out, nmemb) \
    testVariableInput((a), (b), (out), (nmemb), sizeof(*(out)), \
        _Generic((out), float *: add_floats, int *: add_ints, double *: add_doubles));

int main() {
    float a[] = {1, 2, 3};
    float b[] = {4, 5, 6};
    float c[] = {0, 0, 0};
    testVariableInput(a, b, c, 3, sizeof(float), add_floats);
    testVariableInput_g(a, b, c, 3);
}

With the help of _Generic, we can also automagically infer what function callback to pass to the function for limited number of types. Also it's easy to handle new, custom types to the function, without changing it's logic.

Upvotes: 0

chux
chux

Reputation: 153507

Or maybe there is a simpler way of archiving this?

Consider option would be to have a function for each type that is called by the same function.

void testVariableInput_int(const int *a, const int *b, int *out, int m) {
  while (m > 0) {
    m--;
    out[m] = a[m] + b[m];
  }
}

// Like-wise for the other 2
void testVariableInput_float(const float *a, const float *b, float *out, int m) {...} 
void testVariableInput_double(const double *a, const double *b, double *out, int m){...}


void testVariableInput(void *a, void *b, void *out, int m, int type) {
  switch (type) {
    case 1 : testVariableInput_int(a, b, out, m); break;
    case 2 : testVariableInput_float(a, b, out, m); break;
    case 3 : testVariableInput_double(a, b, out, m); break;
  }
}

Sample use

float a[] = {1, 2, 3};
float b[] = {4, 5, 6};
float c[] = {0, 0, 0};
#define N (sizeof c/sizeof c[0])
#define TYPE_FLOAT 2
testVariableInput(a, b, c, N, TYPE_FLOAT);

In C, drop unneeded casting by taking advantage that a void * converts to any object pointer without a cast as well as any object pointer converts to a void * without a cast too.

Advanced

Research _Generic to avoid the need for int type.

Untested sample code:

#define testVariableInput(a, b, c) _Generic(*(a), \
  double: testVariableInput_double, \
  float: testVariableInput_float, \
  int: testVariableInput_int, \
  default: testVariableInput_TBD, \
  )((a), (b), (c), sizeof (a)/sizeof *(a))

    float a[] = {1, 2, 3};
    float b[] = {4, 5, 6};
    float c[] = {0, 0, 0};
    testVariableInput(a, b, c);

_Generic is a bit tricky to use. For OP I recommend sticking with the non-_Generic approach.

Upvotes: 3

Related Questions