Ну, очевидно, один из способов параметризации типов - это использование препроцессора, например:
#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;
}
. Он реализует две функции: divide_double
и divide_int
.В более сложном (реалистичном) примере реализация может быть в отдельном файле компиляции, который компилируется (или включается) отдельно для каждого типа с определенным DIVIDE_TYPE
.
Недостатком по сравнению с реальными обобщениями является то, чтореализации для разных типов не генерируются автоматически, то есть DIVIDE_CALL(mytype, x, y)
не приводит к генерации реализации для mytype
.(Конечно, это можно сделать с помощью некоторых относительно простых сценариев, но затем можно утверждать, что вы на самом деле больше не используете C, и есть языки с более красивыми встроенными обобщениями. =)
В любом случае этоможет работать для структур данных и таких, где требуется фактический тип данных (не указатель void *
).