是否有可能对#define宏进行类型检查?例如:
typedef enum
{
REG16_A,
REG16_B,
REG16_C
}REG16;
#define read_16(reg16) read_register_16u(reg16); \
assert(typeof(reg16)==typeof(REG16));
Run Code Online (Sandbox Code Playgroud)
上面的代码似乎不起作用.我究竟做错了什么?
顺便说一下,我正在使用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; })
Run Code Online (Sandbox Code Playgroud)
但它不允许您比较两种类型.注意虽然指针比较会产生警告 - 你可以做这样的类型检查(也来自linux内核)
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \
})
Run Code Online (Sandbox Code Playgroud)
大概你可以做类似的事情 - 即比较指向参数的指针.
对于整数相关类型,C中的类型检查有点松散; 但是你可以通过使用大多数指针类型不兼容的事实来欺骗编译器.
所以
#define CHECK_TYPE(var,type) { __typeof(var) *__tmp; __tmp = (type *)NULL; }
Run Code Online (Sandbox Code Playgroud)
如果类型不同,这将发出警告"从不兼容的指针类型分配".例如
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
}
Run Code Online (Sandbox Code Playgroud)
我确信有一些方法可以为此获得编译器错误.
小智 5
这是一个老问题,但我相信我有一个通用的答案,根据编译器资源管理器,它可以在 MSVC、gcc 和 clang 上工作。
#define CHECK_TYPE(type,var) { typedef void (*type_t)(type); type_t tmp = (type_t)0; if(0) tmp(var);}
Run Code Online (Sandbox Code Playgroud)
在每种情况下,如果类型不兼容,编译器都会生成有用的错误消息。这是因为它强加了用于函数参数的相同类型检查规则。
它甚至可以在同一范围内多次使用而不会出现问题。这部分让我有些惊讶。(我以为我将不得不利用“__LINE__”来获得这种行为)
下面是我运行的完整测试,注释掉的行都会产生错误。
#include <stdio.h>
#define CHECK_TYPE(type,var) { typedef void (*type_t)(type); type_t tmp = (type_t)0; if(0) tmp(var);}
typedef struct test_struct
{
char data;
} test_t;
typedef struct test2_struct
{
char data;
} test2_t;
typedef enum states
{
STATE0,
STATE1
} states_t;
int main(int argc, char ** argv)
{
test_t * var = NULL;
int i;
states_t s;
float f;
CHECK_TYPE(void *, var); //will pass for any pointer type
CHECK_TYPE(test_t *, var);
//CHECK_TYPE(int, var);
//CHECK_TYPE(int *, var);
//CHECK_TYPE(test2_t, var);
//CHECK_TYPE(test2_t *, var);
//CHECK_TYPE(states_t, var);
CHECK_TYPE(int, i);
//CHECK_TYPE(void *, i);
CHECK_TYPE(int, s); //int can be implicitly used instead of enum
//CHECK_TYPE(void *, s);
CHECK_TYPE(float, s); //MSVC warning only, gcc and clang allow promotion
//CHECK_TYPE(float *, s);
CHECK_TYPE(float, f);
//CHECK_TYPE(states_t, f);
printf("hello world\r\n");
}
Run Code Online (Sandbox Code Playgroud)
在每种情况下,带有 -O1 及更高版本的编译器确实删除了结果代码中宏的所有痕迹。
使用 -O0 MSVC 将对该函数的调用保留为零,但它在无条件跳转中被敲击,这意味着这不应该是一个问题。gcc 和带有 -O0 的 clang 都删除了除了 tmp 变量的堆栈初始化为零之外的所有内容。