I am trying to write some reusable generic type-safe code in C, using macros, similar to how klib works:
#define Fifo_define(TYPE) \
\
typedef struct { \
TYPE *head; \
TYPE *tail; \
size_t capacity; \
} Fifo_##TYPE, *pFifo_##TYPE; \
\
inline Fifo_##TYPE * Fifo_##TYPE##_init(size_t capacity) { \
Fifo_##TYPE * fifo = calloc(1, sizeof(Fifo_##TYPE)); \
TYPE * data = calloc(capacity, sizeof(TYPE)); \
fifo->head = data; \
fifo->tail = data; \
fifo->capacity = capacity; \
}
// define macros
#define Fifo(TYPE) Fifo_##TYPE
#define Fifo_init(TYPE, capacity) Fifo_##TYPE_init(capacity)
And then I just use it with any type parameter:
Fifo_define(int32_t);
...
Fifo(int32_t) *myFifo = Fifo_init(int32_t, 100);
However, writing this is rather convoluted and error prone, with no IDE editor support (IntelliSense), so I wondered if there are any tricks which might allow me to (perhaps) add a few defines and then include the file, without having to end each line with \
?
Something like:
// no idea how to do this, just checking if similar concept is possible
#define FIFO_TYPE int
#define FIFO_NAME Fifo_int
#include <generic-fifo.h>
#undef FIFO_NAME
#undef FIFO_TYPE
And I would somehow get all the right struct
s and functions. The problem is that there is a lot of parameter concatenation in these macros, so I am not sure if this can be done in a simpler manner than the first snippet?
Not really recommended in this case, but you can do something like what you want to achieve with X-macros:
#define SUPPORTED_TYPES \
X(int) \
X(double) \
X(char)
#define X(TYPE) \
typedef struct { \
TYPE *head; \
TYPE *tail; \
size_t capacity; \
} Fifo_##TYPE, *pFifo_##TYPE;
SUPPORTED_TYPES
#undef X
#define X(TYPE) \
inline Fifo_##TYPE * Fifo_##TYPE##_init(size_t capacity) \
{ \
Fifo_##TYPE * fifo = calloc(1, sizeof(Fifo_##TYPE)); \
TYPE * data = calloc(capacity, sizeof(TYPE)); \
fifo->head = data; \
fifo->tail = data; \
fifo->capacity = capacity; \
}
SUPPORTED_TYPES
#undef X
But this didn't really improve the situation all that much. It got rid of the need for a single, ugly Fifo_define
macro, so you can split up the code in several sections. But the macro mess remains.
I would recommend some completely different approach. Two suggestions:
Handle the type-generic things in the classic C way, in run-time. Use callbacks. Keep track of the used type with an enum, if needed.
C11 _Generic
allows all kinds of type safety tricks and can be used to phase out such messy macros. Example that implements "functors". The macro itself is kept minimal and the different implementations for various types is typed out. (That's usually what you end up doing anyway, when you do type-generic programming.)