功能说明
用于在编译时检查一个条件是否为真,如果条件为假则会编译失败,编译器报错
#define TU_STRCAT(a, b) a##b ///< concat without expand
#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
#define _TU_COUNTER_ __LINE__
#define TU_VERIFY_STATIC(const_expr, _mess) enum \
{
\
TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1 / (!!(const_expr)) \
}
TU_VERIFY_STATIC(1, "static verify successful\r\n");
编译成功,无报错TU_VERIFY_STATIC(0, "static verify failed\r\n");
编译失败,有报错
示例
#include <stdio.h>
#include "stdint.h"
#include <stdbool.h>
#define TU_STRCAT(a, b) a##b ///< concat without expand
#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
#define _TU_COUNTER_ __LINE__
#define TU_VERIFY_STATIC(const_expr, _mess) enum \
{
\
TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1 / (!!(const_expr)) \
}
typedef struct __attribute__((packed))
{
uint8_t cmd_code;
uint8_t reserved[6];
uint16_t alloc_length;
uint8_t control;
} scsi_read_format_capacity_t;
TU_VERIFY_STATIC(sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
TU_VERIFY_STATIC(1, "static verify successful\r\n");
TU_VERIFY_STATIC(0, "static verify failed\r\n");
int main(int argc, char *argv[])
{
printf("hello tyustli\r\n");
return 0;
}
结果打印
test.c:12:51: warning: division by zero [-Wdiv-by-zero]
12 | TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1 / (!!(const_expr)) \
| ^
test.c:25:1: note: in expansion of macro ‘TU_VERIFY_STATIC’
25 | TU_VERIFY_STATIC(0, "static verify failed\r\n");
| ^~~~~~~~~~~~~~~~
test.c:12:16: error: enumerator value for ‘_verify_static_25’ is not an integer constant
12 | TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1 / (!!(const_expr)) \
| ^~~~~~~~~~~~~~~
test.c:5:25: note: in definition of macro ‘TU_STRCAT’
5 | #define TU_STRCAT(a, b) a##b ///< concat without expand
| ^
test.c:12:5: note: in expansion of macro ‘TU_XSTRCAT’
12 | TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1 / (!!(const_expr)) \
| ^~~~~~~~~~
test.c:25:1: note: in expansion of macro ‘TU_VERIFY_STATIC’
25 | TU_VERIFY_STATIC(0, "static verify failed\r\n");
解析
改宏主要利用了 enum 枚举成员必须是整数的特性
TU_XSTRCAT(_verify_static_, _TU_COUNTER_)
这一句宏是将_verify_static_
和__LINE__
连接在一起,用于生成一个独一无二的名称,例如_verify_static_15
,_verify_static_16
。- 在一个文件中,行数肯定是不会重复的,所以编译不会报错
- 在多个文件中,行数会重复,由于该变量并未使用,枚举类型中的成员重名,并不会报错。
!!(const_expr)
条件const_expr
进行两次逻辑非运算,得到逻辑值
结果 0 或者 1。如果表达式const_expr
的结果为 0 ,则得到 0 , 如果为非 0 值,则得到 1- 上一步得到一个逻辑 0 或者逻辑 1,用
1/1
不会报错,1/0
编译会报错。
TU_XSTRCAT
为什么用两级?
如果直接用 TU_STRCAT(_verify_static_, _TU_COUNTER_)
,由于 ##
是用于宏参数的连接,则结果为 _verify_static__TU_COUNTER_
如果使用 TU_XSTRCAT(_verify_static_, _TU_COUNTER_)
则宏展开时,两个参数分别为 _verify_static_
_TU_COUNTER_
。这两个参数又作为参数传递给 TU_STRCAT
,所以连接的就是 _verify_static_
和 真正的数字