在C中的Typechecking宏参数
是否可以对#define宏进行类型检查? 例如:
typedef enum { REG16_A, REG16_B, REG16_C }REG16; #define read_16(reg16) read_register_16u(reg16); \ assert(typeof(reg16)==typeof(REG16));
上面的代码似乎不起作用。 我究竟做错了什么?
顺便说一下,我正在使用gcc,我可以保证在这个项目中我将一直使用gcc。 代码不需要是可移植的。
gcc支持typeof
例如,从linux内核中获取的类型安全的min宏
#define min(x,y) ({ \ typeof(x) _x = (x); \ typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x < _y ? _x : _y; })
但它不允许你比较两种类型。 注意虽然会产生警告的指针比较 - 你可以做这样的类型检查(也来自linux内核)
#define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ })
大概你可以做类似的事情 - 即比较指向参数的指针。
对于整数相关类型,C中的类型检查有点松散; 但是你可以通过使用大多数指针类型不兼容的事实来欺骗编译器。
所以
#define CHECK_TYPE(var,type) { __typeof(var) *__tmp; __tmp = (type *)NULL; }
如果类型不相同,这将发出警告“从不兼容的指针类型分配”。 例如
typedef enum { A1,B1,C1 } my_enum_t; int main (int argc, char *argv) { my_enum_t x; int y; CHECK_TYPE(x,my_enum_t); // passes silently CHECK_TYPE(y,my_enum_t); // assignment from incompatible pointer type }
我确信有一些方法可以为此获得编译器错误。
不,宏不能为您提供任何类型检查。 但是,毕竟,为什么宏? 您可以编写一个static
inline
函数(可能)将由编译器内联 – 在这里您将进行类型检查。
static inline void read_16(REG16 reg16) { read_register_16u(reg16); }
要继续ulidtko的想法,采用inline
函数并让它返回一些东西:
inline bool isREG16(REG16 x) { return true; }
有了这样的东西,你可以做编译时断言:
typedef char testit[sizeof(isREG16(yourVariable))];
不.C中的宏本质上是类型不安全的,并且试图检查C中的类型是否充满问题。
首先,在编译阶段通过文本替换扩展宏,其中没有可用的类型信息。 因此,编译器在进行宏扩展时检查参数的类型是完全不可能的。
其次,当您尝试在扩展代码中执行检查时,如问题中的assert
,您的检查将延迟到运行时,并且还会触发看似无害的构造,如
a = read_16(REG16_A);
因为枚举数( REG16_A
, REG16_B
和REG16_C
)的类型是int
而不是REG16
类型。
如果您想要类型安全,最好的办法是使用一个function。 如果您的编译器支持它,您可以声明函数inline
,因此编译器知道您希望尽可能避免函数调用开销。