在C中的Typechecking宏参数

Roc*_*net 4 c macros

是否有可能对#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.代码不需要是可移植的.

Dip*_*ick 8

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)

大概你可以做类似的事情 - 即比较指向参数的指针.


use*_*691 7

对于整数相关类型,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 变量的堆栈初始化为零之外的所有内容。