我想在错误的情况下实现一个"断言"来阻止编译,而不是在运行时失败.
我目前有一个这样的定义,它工作得很好,但它增加了二进制文件的大小.
#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}
示例代码(无法编译).
#define DEFINE_A 1 #define DEFINE_B 1 MY_COMPILER_ASSERT(DEFINE_A == DEFINE_B);
我如何实现它,以便它不生成任何代码(为了最小化生成的二进制文件的大小)?
纯标准C中的编译时断言是可能的,并且一些预处理器技巧使其使用看起来像运行时的使用一样干净assert()
.
关键技巧是找到一个可以在编译时进行评估的结构,并且可能会导致某些值出错.一个答案是数组的声明不能具有负大小.使用typedef可防止成功时分配空间,并在失败时保留错误.
错误消息本身将隐式引用负大小的声明(GCC称"数组foo的大小为负"),因此您应该为数组类型选择一个名称,该名称暗示此错误确实是断言检查.
要处理的另一个问题是,只能typedef
在任何编译单元中使用特定类型名称.因此,宏必须安排每个用法以获取要声明的唯一类型名称.
我通常的解决方案是要求宏有两个参数.第一个是assert的条件为true,第二个是在幕后声明的类型名称的一部分.plinth的答案提示使用令牌粘贴和__LINE__
预定义的宏来形成唯一的名称,可能不需要额外的参数.
不幸的是,如果断言检查在包含文件中,它仍然可能与第二个包含文件中相同行号的检查冲突,或者与主源文件中的该行号冲突.我们可以通过使用宏来解决这个问题__FILE__
,但它被定义为一个字符串常量,并且没有预处理器技巧可以将字符串常量转换回标识符名称的一部分; 更不用说合法文件名可以包含不是标识符合法部分的字符.
所以,我建议使用以下代码片段:
/** A compile time assertion check. * * Validate at compile time that the predicate is true without * generating code. This can be used at any point in a source file * where typedef is legal. * * On success, compilation proceeds normally. * * On failure, attempts to typedef an array type of negative size. The * offending line will look like * typedef assertion_failed_file_h_42[-1] * where file is the content of the second parameter which should * typically be related in some obvious way to the containing file * name, 42 is the line number in the file on which the assertion * appears, and -1 is the result of a calculation based on the * predicate failing. * * \param predicate The predicate to test. It must evaluate to * something that can be coerced to a normal C boolean. * * \param file A sequence of legal identifier characters that should * uniquely identify the source file in which this condition appears. */ #define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file) #define _impl_PASTE(a,b) a##b #define _impl_CASSERT_LINE(predicate, line, file) \ typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];
典型用法可能是这样的:
#include "CAssert.h" ... struct foo { ... /* 76 bytes of members */ }; CASSERT(sizeof(struct foo) == 76, demo_c);
在GCC中,断言失败看起来像:
$ gcc -c demo.c demo.c:32: error: size of array `assertion_failed_demo_c_32' is negative $
以下COMPILER_VERIFY(exp)
宏工作得相当好.
// combine arguments (after expanding arguments) #define GLUE(a,b) __GLUE(a,b) #define __GLUE(a,b) a ## b #define CVERIFY(expr, msg) typedef char GLUE (compiler_verify_, msg) [(expr) ? (+1) : (-1)] #define COMPILER_VERIFY(exp) CVERIFY (exp, __LINE__)
它适用于C和C++,可以在任何允许使用typedef的地方使用.如果表达式为true,则为1 char的数组生成typedef(这是无害的).如果表达式为false,则为-1个字符的数组生成typedef,这通常会导致错误消息.作为一个arugment给出的表达式可以是任何评估为编译时常量的表达式(因此涉及sizeof()的表达式可以正常工作).这使它比它更灵活
#if (expr) #error #endif
您可以将其限制为可由预处理器评估的表达式.