7

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.

  • 2
    For a problem caused by a related, but not entirely identical, arrangement that I once thought about, see http://stackoverflow.com/questions/2904668/compatible-types-and-structures-in-c. +1 for an interesting question, though. – Oliver Charlesworth Aug 15 '11 at 19:04
  • What are you trying to do? There's no catchall optimal solution. – Foo Bah Aug 15 '11 at 19:07
  • I am writing a program that works with graphs. I want to make my graph algorithms reusable. Graph nodes can have some additional information, this is why I want to keep an additional member of arbitrary type in the node's structure. –  Aug 15 '11 at 19:09
  • 1
    You could put your `#define`, as well as invokations of the macro for some common types, into a header, so that you don't have to use the macro as often. – Chris Lutz Aug 15 '11 at 19:21
  • 5
    C is not a huge fan of generic programming. You'll hate me for asking, but is there a reason you don't want to/can't use C++? – Jonathan Grynspan Aug 15 '11 at 19:24
  • Well, to tell the truth there is no real reason to *not* use C++ for the task at hand, except that what I am doing is a self-imposed task, not an assignment: I'm more of a C++ guy, and I'm trying to learn to code in ANSI C, and to do so I picked a problem ans started working on it. –  Aug 15 '11 at 19:42
  • @Grigory - But this way you are **not** learning to code in C. You are still using C++, just doing it in a harder and uglier way. C is all about pointers, malloc, printf, strlen, `void**` and strcpy. Get down to that, and you'll soon return to C++ again! – Bo Persson Aug 16 '11 at 09:08

3 Answers3

4

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!

James O'Doherty
  • 2,186
  • 13
  • 14
2

Noting that templates in c++ provide a language for writing code, you might simple consider doing code generation with some tool more powerful than the c-preprocessor.

Now that does add another step to you build, and makes you build depend on another toll (unless you care to write your own generator in c...), but it may provide the flexibility and type-safety you desire.

dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
1

This is almost the same, but it's a bit more nimble:

#define PAIR_T(TYPE) \
    struct { \
        TYPE x; \
        TYPE y; \
    }


typedef PAIR_T(int) int_pair;
typedef PAIR_T(const char *) string_pair;

int main(void)
{
    int_pair p = {1, 1};
    string_pair sp = {"a", "b"};
}
bluss
  • 12,472
  • 1
  • 49
  • 48