Well, obviously one way to parametrize types is to use the preprocessor, e.g.:
#define DIVIDE_FUNC(type) divide_##type
#define DIVIDE_CALL(type, a, b) DIVIDE_FUNC(type)((a), (b))
#define DIVIDE_DECL(type) type DIVIDE_FUNC(type)(type a, type b)
#define DIVIDE_IMPLEMENTATION DIVIDE_DECL(DIVIDE_TYPE) { return a / b; }
#define DIVIDE_TYPE int
DIVIDE_IMPLEMENTATION
#undef DIVIDE_TYPE
#define DIVIDE_TYPE double
DIVIDE_IMPLEMENTATION
#include <stdio.h>
int main (void) {
int i = 5, j = 2;
(void) printf("int %d / %d = %d\n", i, j, DIVIDE_CALL(int, i, j));
(void) printf("double %d / %d = %f\n", i, j, DIVIDE_CALL(double, i, j));
return 0;
}
This implements two functions: divide_double
and divide_int
. In a more complex (realistic) example the implementation could be in a separate compilation file which is compiled (or included) separately for each type with a different DIVIDE_TYPE
defined.
The downside compared to real generics is that the implementations for different types are not automatically generated, i.e. DIVIDE_CALL(mytype, x, y)
does not cause the implementation for mytype
to be generated. (Of course this could be arranged with some relatively simple scripting, but then one might argue that you are not really using C anymore and there are languages with prettier built-in generics. =)
In any case, this can work for data structures and such where the actual data type (not a void *
pointer) is desired.