我正在尝试使用宏在C中编写一些可重用的通用类型安全代码,类似于klib的工作方式:
#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)
然后我只使用任何类型参数:
Fifo_define(int32_t);
...
Fifo(int32_t) *myFifo = Fifo_init(int32_t, 100);
然而,写这个是相当复杂且容易出错的,没有IDE编辑器支持(IntelliSense),所以我想知道是否有任何技巧可能允许我(可能)添加一些定义然后包含文件,必须以\
结束每一行?
类似的东西:
// 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
我会以某种方式获得所有正确的struct
和功能。问题是这些宏中有很多参数连接,所以我不确定这是否可以比第一个片段更简单的方式完成?
答案 0 :(得分:1)
在这种情况下并不是真的推荐,但是您可以使用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
但这并没有真正改善这种情况。它摆脱了对单个丑陋的Fifo_define
宏的需求,因此您可以将代码分成几个部分。但宏观问题仍然存在。
我会推荐一些完全不同的方法。两个建议:
在运行时以经典C方式处理类型泛型。使用回调。如果需要,使用枚举跟踪使用的类型。
C11 _Generic
允许各种类型的安全技巧,可以用来淘汰这些凌乱的宏。 Example that implements "functors"。宏本身保持最小,并且键入了各种类型的不同实现。 (这通常是你最后做的事情,当你进行类型通用编程时。)
答案 1 :(得分:1)
如果您使用的是复杂的宏,请考虑使用m4而不是C预处理器。 m4 is similar to the C pre-processor but is much more powerful并且可以执行多行没有行继续符的事情。
使用像m4这样的代码生成器称为 meta-programming 。
在C中使用m4可以通过将其视为预处理前like this来实现:
% grep -v '#include' file1 file2 | m4 > outfile
% m4 file1 file2 | cc
由于m4的工作方式与基本级别的C预处理器类似,因此除了支持自己的高级功能外,它通常还可以正确转换任何普通的C宏。