1

This is not a 'How can a mixed data type (int, float, char, etc) be stored in an array?' question read carefully please! Suppose I have the following void pointer, something I don't know it's type until runtime:

void* data;

now I know I can do the following, when I know the type of data(e.g. int):

int typed_data = *(int*)data;

using a switch case statement I could check a variable to determine which cast to perform:

switch(type_id) {
    case INT:
        int typed_data = *(int*)data;
        break;
    case FLOAT:
        float typed_data = *(float*)data;
        break;
    // ...
    // etc.
}

But, this way I will not be able to access typed_data outside the switch block, consider the below funstion as an example; It takes two void pointers, and according to the value of type_id, it casts the s and x to correct data types, and then does other things with the newly defined typed data:

int sequential_seach(int n, void* s, void* x, type_id) {
    int location = 0;
    switch(type_id) {
        case INT:
            int *list = s;
            int element = *(int*)x;
            break;
        case FLOAT:
            float *list = s;
            float element = *(float*)x;
            break;
            // ...
            // etc.
    }

    while(location < n && list[location] != element) { // <---This will cause a compile error

        location++;
        if(location > n - 1) {
            location = -1;
        }
    }
    return location;
}

In the above function location and list are not accessible outside the swtich block, even if type_id matched one of the case values and they were defined, they are still out of scope, outside the switch block, therefore when the compiler reaches the line while resides, it complains that location and list are not defined. But these typed variables are needed for the function. So how to solve this? should I copy paste the while block into every case? That doesn't look it's a very good solution. What if I had a longer code which needed these variables in 100 different places?

ilgaar
  • 804
  • 1
  • 12
  • 31
  • 2
    Sounds like you want a different language. – Scott Hunter Dec 21 '15 at 20:51
  • 2
    Would a `union`, work for you? You could keep a struct that was a pair of type and a union that allows you to access it as any of the possible types. – Timothy Murphy Dec 21 '15 at 20:52
  • Why are you trying this? [tag:c] is not well suited for such a dynamic type system, you might want to try a different language or a different approach to solve the problem. Also, as someone already mentioned, a `union` might be a better choice. Dereferencing the pointers like that is very dangerous because you are potentially invoking undefined behavior. – Iharob Al Asimi Dec 21 '15 at 20:55
  • 1
    @TimothyMurphy: You'd still need distinct code to process each of the types in the union, and means of determining which type to use. – Scott Hunter Dec 21 '15 at 20:56
  • This is not exactly a duplicate. – ilgaar Dec 21 '15 at 22:03

2 Answers2

1

Sounds like you need generics: the ability to define functions with compile-time type parameters.

Unfortunately, C doesn't natively have generics. Fortunately, you can use macros as pseudo-generics to make the preprocessor automatically generate multiple versions of your code.

Adapted from the linked answer:

// sequential_search.h

/* Provide some helpers that generate a name of the form of sequential_search_T,
    unique for each type argument */

#define TOKENPASTE(x, y) x ## y    
#define SEQ_SEARCH(T) TOKENPASTE(sequential_search_, T)

/* Provide the generic type definition of your function */

int SEQ_SEARCH(TYPE) (int n, void* s, void* x) {
    int location = 0;
    TYPE* list = s;
    TYPE element = *(TYPE*)x;

    while(location < n && list[location] != element) {
        location++;
        if(location > n - 1) {
            location = -1;
        }
    }

    return location;
}

Instantiate it once for each type argument you intend to pass:

// sequential_search.c

#define TYPE int
#include "sequential_search.h"
#undef TYPE

#define TYPE float
#include "sequential_search.h"
#undef TYPE

// etc.

Finally, create a (statically resolvable) call spot that will switch on the type id you have (the runtime information) and then immediately dispatch to one of the generic versions:

int sequential_search(int n, void* s, void* x, type_id) {
    switch(type_id) {
        case INT: return sequential_search_int(n, s, x);
        case FLOAT: return sequential_search_float(n, s, x);
        // etc.
    }
}
Community
  • 1
  • 1
Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104
  • Why do I get compiler errors on this line: `int SEQ_SEARCH(int n, void* s, void* x)`? I get two errors: `Expected ';' after top level declaration,` and `Too many arguments provided to function-like macro invocation` Also I get an error on line `int sequential_search(...)` which is `ISO C requires a named parameter before '...'` – ilgaar Dec 21 '15 at 21:48
  • @igaar Regarding error #1: see the edit, I had a typo in the initial definition. Regarding error #2: you're right, I didn't literally mean `...`. I meant to say "provide your actual arguments here", but `...` is valid C syntax for variadic functions. See the edit now, it should be clearer. – Theodoros Chatzigiannakis Dec 21 '15 at 21:53
  • Now I get `use of undeclared identifier 'TYPE'` in my header file and therefor `use of undeclared identifier 'list'` – ilgaar Dec 21 '15 at 22:01
  • @igaar Make sure you don't `#include "sequential_search.h"` in your code, because it will cause the error you're seeing. Instead, `#include "sequential_search.c"` and let *that* include the header file. The first file needs `TYPE` already defined, and that's what the second file does. – Theodoros Chatzigiannakis Dec 21 '15 at 22:08
-1

It's not possible to cast a void pointer at runtime in C.

And your code doesn't check if the cast is valid either. Doing this, you will either lose some data or risk a segmentation fault since data types aren't all the same size.

loginn
  • 94
  • 1
  • 8