Search code examples
ctypesgeneric-programming

Which way is better for creating type-agnostic structures in C?


I'm trying to write some generic structures. Essentially, what I need for my purpose is C++ templates, but since I'm writing in C, templates are out of consideration. Currently I'm considering 2 ways of achieving what I want.

Method 1: use the preprocessor. Like so:

#define DEFINE_PAIR(T) typedef struct Pair_##T{ \
                           T x; \
                           T y; \
                        } Pair_##T

DEFINE_PAIR(int);

int main(){
   Pair_int p;
   return 0;
}

An obvious downside to it is that you have to invoke the macro before using the type. Probably there are more disadvantages, which I hope you will point out.

Method 2: just use void-pointers, like so:

typedef struct Pair{
   void* x;
   void* y;
} Pair;

Obviously, this approach is not type safe (I could easily pass a pair of strings to a function expecting a pair of doubles), plus the code doing deallocation gets a lot messier with this approach.

I would like to hear your thoughts on this. Which of the two methods is better/worse and why? Is there any other method I could use to write generic structures in C?

Thanks.


Solution

  • If you only plan on using primitive data types, then your original macro-based solution seems nifty enough. However, when you start storing pairs of pointers to opaque data types with complex structures underneath that are meant to be used by passing pointers between functions, such as:

    complex_structure_type *object = complex_structure_type_init();
    complex_structure_type_set_title(object, "Whatever");
    complex_structure_type_free(object);
    

    then you have to

    typedef complex_structure_type *complex_structure_type_ptr; 
    

    in order to

    DEFINE_PAIR(complex_structure_type_ptr);
    

    so you can

    Pair_complex_structure_type_ptr p;
    

    and then

    p.x = object;
    

    But that's only a little bit more work, so if you feel it works for you, go for it. You might even put together your own preprocessor that goes through the code, pulls out anything like Pair_whatever, and then adds DEFINE_PAIR(whatever) for the C preprocessor. Anyway, it's definitely a neat idea that you've presented here.

    Personally, I would just use void pointers and forget about strong type safety. C just doesn't have the same type safety machinery as other languages, and the more opportunities you give yourself to forget something, the more bugs you'll accidentally create.

    Good luck!