Nor*_*ame 84
这适用于功能和非功能范围(但不在结构,联合内部).
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
STATIC_ASSERT(1,this_should_be_true);
int main()
{
STATIC_ASSERT(1,this_should_be_true);
}
Run Code Online (Sandbox Code Playgroud)
如果编译时断言无法匹配,则GCC会生成几乎可理解的消息 sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative
可以或应该更改宏以生成typedef的唯一名称(即名称__LINE__末尾的连接static_assert_...)
这也可以使用,而不是三元,这#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]甚至可以在生锈的olde cc65(用于6502 cpu)编译器上工作.
更新:
为了完整起见,这里是版本__LINE__
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
// token pasting madness:
#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)
#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)
#define COMPILE_TIME_ASSERT(X) COMPILE_TIME_ASSERT2(X,__LINE__)
COMPILE_TIME_ASSERT(sizeof(long)==8);
int main()
{
COMPILE_TIME_ASSERT(sizeof(int)==4);
}
Run Code Online (Sandbox Code Playgroud)
UPDATE2:GCC特定代码
GCC 4.3(我猜)引入了"错误"和"警告"功能属性.如果通过死代码消除(或其他措施)无法消除对具有该属性的函数的调用,则会生成错误或警告.这可以用于使用用户定义的故障描述来编译时间断言.它仍然是确定如何在名称空间范围内使用它们而不需要使用虚函数:
#define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; })
// never to be called.
static void my_constraints()
{
CTC(sizeof(long)==8);
CTC(sizeof(int)==4);
}
int main()
{
}
Run Code Online (Sandbox Code Playgroud)
这就是它的样子:
$ gcc-mp-4.5 -m32 sas.c
sas.c: In function 'myc':
sas.c:7:1: error: call to 'compile_time_check' declared with attribute error: assertion failure: `sizeof(int)==4` not true
Run Code Online (Sandbox Code Playgroud)
ems*_*msr 80
C11标准添加了_Static_assert关键字.
_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */
Run Code Online (Sandbox Code Playgroud)
第一个槽需要是一个整数常量表达式.第二个槽是一个常量字符串文字,可以是long(_Static_assert(0, L"assertion of doom!")).
我应该注意到,这也是在最新版本的clang中实现的.
Gab*_*les 17
作为复活节礼物,这个答案在 2022 年 4 月 17 日得到了极大的改善。我相信这是这里最彻底和完整的答案,因为我已经制作了 3 个不同复杂度的独立解决方案来涵盖不同版本的 C 和 C++,而且由于我提供的最后一个版本涵盖了C 和 C++ 的所有版本,最初是看似不可能的壮举。
\n您可以在我的文件static_assert_for_all_versions_of_c_and_cpp.c中查看和测试以下所有版本的 C 和 C++ 代码。
\n请注意C++ style comments are not allowed in ISO C90,因此我的代码示例必须仅使用 C 样式注释 ( /* */),而不是 C++ 样式注释,以便我的代码也//能够编译。-std=c90
STATIC_ASSERT(test_for_true):如果您想要一个快速且超级简单的宏可以在任何版本的 C(使用 gcc 编译时)或C++11 或更高版本的任何版本的 C++ 中工作,请参阅我在底部的两个简单的宏块下一节:“C 和 C++ 中可用的静态断言声明摘要”。以下是为方便起见而复制和粘贴的宏,后面是适用于任何版本的 C 或 C++ 的更复杂但通用的STATIC_ASSERT(test_for_true)宏。这是我的3个主要解决方案:
[迄今为止最简单的选项!]仅STATIC_ASSERT(test_for_true)适用于C11 或更高版本以及仅 C++11 或更高版本:
#include <assert.h>\n#define STATIC_ASSERT(test_for_true) \\\n static_assert((test_for_true), "(" #test_for_true ") failed")\nRun Code Online (Sandbox Code Playgroud)\n或者 [我的偏好],STATIC_ASSERT(test_for_true)对于任何版本的 C(使用 gcc 编译器时作为 gcc 扩展),包括 C89/C90、C99、C11、C17 等,以及C++11 或更高版本(无法处理旧版本) C++):
#ifdef __cplusplus\n #ifndef _Static_assert\n #define _Static_assert static_assert\n #endif\n#endif\n\n// Option 1: static assert for which you provide the failure message\n#define STATIC_ASSERT(test_for_true, failure_message) \\\n _Static_assert((test_for_true), failure_message)\n\n// Option 2: static assert for which the failure message is auto-generated\n#define STATIC_ASSERT2(test_for_true) \\\n _Static_assert((test_for_true), "(" #test_for_true ") failed")\nRun Code Online (Sandbox Code Playgroud)\nSTATIC_ASSERT(test_for_true)对于任何版本的 C 和任何版本的 C++:
如果您希望单个STATIC_ASSERT宏适用于所有版本的 C 和 C++,我将在下面以“我的最终版本”开头的部分中介绍它。您可以在底部的“测试摘要”部分中查看我用来测试它的构建命令和语言设置。让静态断言在C++11 之前的版本中工作,例如 C++98、C++03 等,是困难的部分!下面的宏_Static_assert_hack是处理那些早期版本的 C++ 的。以下是处理所有版本的 C 和 C++ 的完整代码块,为了方便起见,删除了大部分注释:
// See: https://stackoverflow.com/a/54993033/4561887\n\n#define CONCAT_(prefix, suffix) prefix##suffix\n#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)\n#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)\n\n/* Static assert hack required for **pre-C++11**, such as C++98, C++03, etc.\n * - It works only with C++, NOT with C! \n */\n#define _Static_assert_hack(expression, message) \\\n struct MAKE_UNIQUE_VARIABLE_NAME(static_assertion_failed) \\\n { \\\n _Pragma("GCC diagnostic push") \\\n _Pragma("GCC diagnostic ignored \\"-Wunused-local-typedefs\\"") \\\n typedef char static_assertion_failed[(expression) ? 1 : -1]; \\\n _Pragma("GCC diagnostic pop") \\\n }\n\n/* For C++ only:\n * See: https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html \n */\n#ifdef __cplusplus\n #if __cplusplus < 201103L\n /* for pre-C++11 */\n #ifndef _Static_assert\n #define _Static_assert _Static_assert_hack\n #endif\n #else\n /* for C++11 or later */\n #ifndef _Static_assert\n #define _Static_assert static_assert\n #endif\n #endif\n#endif\n\n/* For C **and** C++: */\n#define STATIC_ASSERT(test_for_true) \\\n _Static_assert((test_for_true), "(" #test_for_true ") failed")\nRun Code Online (Sandbox Code Playgroud)\n知道对于:
\n_Static_assert(expression, message)中可用。\nstatic_assert还可以作为_Static_assert标头中的便捷宏使用<assert.h>,以便与 C++11 中的命名相匹配。因此,要在C11 或更高版本中将类似 C++ 的static_assert宏作为宏,您还应该.#include <assert.h>_Static_assert(expression)(即:没有该message部件)自C23 或更高版本起也可用。static_assert(expression, message)中可用。\nstatic_assert(expression)(即:没有该message部分)也可用于C++17 或更高版本。_Static_assert都支持作为gcc 扩展,包括 c90、c99、c11、c17 等。\nstatic_assert可以将其用作 C11 或更高版本的宏。_Static_assert#include <assert.h>static_assert支持作为C++11 或更高版本的关键字。您不需要#include <assert.h>像在 C 中那样在 C++ 中获得这种格式。\n\n甚至更老的平台也不支持
\nstatic_assert或_Static_assert根本不支持。例如,4.6之前的GCC版本不支持_Static_assert,4.3之前的G++版本不支持static_assert,这是由C11和C++11标准化的。C
\n_Static_assert和 C++static_assert是可以在不包含<assert.h>. Gnulib 替代品是需要包含<assert.h>.
我喜欢编写一个STATIC_ASSERT包装宏来将参数减少到 1 并自动生成message参数,这样我就可以STATIC_ASSERT(expression)代替STATIC_ASSERT(expression, message). 以下是如何轻松做到这一点的方法:
#include <assert.h>\n#define STATIC_ASSERT(test_for_true) \\\n static_assert((test_for_true), "(" #test_for_true ") failed")\nRun Code Online (Sandbox Code Playgroud)\n#ifdef __cplusplus\n #ifndef _Static_assert\n #define _Static_assert static_assert\n #endif\n#endif\n#define STATIC_ASSERT(test_for_true) \\\n _Static_assert((test_for_true), "(" #test_for_true ") failed")\nRun Code Online (Sandbox Code Playgroud)\n在我的static_assert_for_all_versions_of_c_and_cpp.c中测试上述代码片段。
\n对于C11 之前的gcc ,gcc 已经定义了_Static_assert(expression, message),这非常好。因此,只需使用它即可完成,如上所述!但是,如果您不使用 gcc 编译器怎么办?你能做什么?
嗯,我注意到一些非常有趣的事情。如果我_Static_assert(1 > 2, "this should fail");在 C90 中与 gcc 一起使用,请使用以下构建命令:
gcc -Wall -Wextra -Werror -O3 -std=c90 \\\n static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a\nRun Code Online (Sandbox Code Playgroud)\n我收到此失败的编译时错误_Static_assert。这是一个超级奇怪的错误!但这不是一个意外的构建错误,这是静态断言失败错误,因为他们还使用此版本的 C 的 hack来获取编译时静态断言!
In file included from /usr/include/features.h:461,\n from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,\n from /usr/include/stdint.h:26,\n from /usr/lib/gcc/x86_64-linux-gnu/9/include/stdint.h:9,\n from static_assert_for_all_versions_of_c_and_cpp.c:73:\nstatic_assert_for_all_versions_of_c_and_cpp.c: In function \xe2\x80\x98main\xe2\x80\x99:\nstatic_assert_for_all_versions_of_c_and_cpp.c:224:5: error: negative width in bit-field \xe2\x80\x98__error_if_negative\xe2\x80\x99\n 224 | _Static_assert(1 > 2, "this should fail");\n | ^~~~~~~~~~~~~~\nRun Code Online (Sandbox Code Playgroud)\n如果我转到 GitHub 上的 gcc 源代码镜像(https://github.com/gcc-mirror/gcc),克隆存储库,然后__error_if_negative使用 grep 或 ripgrep 进行搜索,我只在一个位置找到结果,这里: https: //github.com/gcc-mirror/gcc/blob/master/libgcc/soft-fp/soft-fp.h#L69-L71:
In file included from /usr/include/features.h:461,\n from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,\n from /usr/include/stdint.h:26,\n from /usr/lib/gcc/x86_64-linux-gnu/9/include/stdint.h:9,\n from static_assert_for_all_versions_of_c_and_cpp.c:73:\nstatic_assert_for_all_versions_of_c_and_cpp.c: In function \xe2\x80\x98main\xe2\x80\x99:\nstatic_assert_for_all_versions_of_c_and_cpp.c:224:5: error: negative width in bit-field \xe2\x80\x98__error_if_negative\xe2\x80\x99\n 224 | _Static_assert(1 > 2, "this should fail");\n | ^~~~~~~~~~~~~~\nRun Code Online (Sandbox Code Playgroud)\n这是一个静态断言 hack,您可以在 C11 之前的 C 的非 gcc 版本中借用和使用!
\n只需替换_FP_STATIC_ASSERT为_Static_assert,如下所示:
# define _FP_STATIC_ASSERT(expr, msg) \\\n extern int (*__Static_assert_function (void)) \\\n [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })]\nRun Code Online (Sandbox Code Playgroud)\n使用_Static_assert上面的 hack 的注意事项:
-std=c90等时-std=c99。\n-std=gnu90-std=gnu99# define _Static_assert(expr, msg) \\\n extern int (*__Static_assert_function (void)) \\\n [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })]\nRun Code Online (Sandbox Code Playgroud)\n...然后你会看到这个关于 的超级神秘错误expected specifier-qualifier-list before \xe2\x80\x98extern\xe2\x80\x99。这并不是因为静态断言表达式失败(为假),而是因为静态断言黑客在此用例中被破坏。请注意,extern上面的 hack 中使用了 ,因此,它显示在错误中:\neRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c90 static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a\nIn file included from /usr/include/features.h:461,\n from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,\n from /usr/include/stdint.h:26,\n from /usr/lib/gcc/x86_64-linux-gnu/9/include/stdint.h:9,\n from static_assert_for_all_versions_of_c_and_cpp.c:73:\nstatic_assert_for_all_versions_of_c_and_cpp.c:193:5: error: expected specifier-qualifier-list before \xe2\x80\x98extern\xe2\x80\x99\n 193 | _Static_assert(2 > 1, "this should pass");\n | ^~~~~~~~~~~~~~\nRun Code Online (Sandbox Code Playgroud)\n我发现在 C++11 之前的 C++ 中获得一个很好的静态断言 hack 非常棘手,但我成功了!它确实是一件艺术品,但它看起来确实有效,而且运行良好且坚固耐用。它也可以像 C++11 一样在结构体和联合体中工作static_assert!这里是。您可以在我的static_assert_for_all_versions_of_c_and_cpp.c中测试它:
typedef union data_u\n{\n data_t data;\n uint8_t bytes[sizeof(data_t)];\n\n _Static_assert(2 > 1, "this should pass");\n _Static_assert(5 > 4, "this should pass");\n} data_union_t;\nRun Code Online (Sandbox Code Playgroud)\nSTATIC_ASSERT()适用于所有版本的 C 和所有版本的 C++只需进行一些调整来更改使用的样式和时间,下面的代码也可以在非 gcc 编译器上与任何版本的 C 和 C++一起使用。
\n正如它所写的,我希望它在使用 gcc/g++ 编译器或LLVM clang 编译器编译时适用于所有版本的 C 和 gnu C 以及所有版本的 C++ 和 gnu++。
\n这是最终版本:一个静态断言可以处理任何版本的 C 或 C++!:
\neRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c90 static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a\nIn file included from /usr/include/features.h:461,\n from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,\n from /usr/include/stdint.h:26,\n from /usr/lib/gcc/x86_64-linux-gnu/9/include/stdint.h:9,\n from static_assert_for_all_versions_of_c_and_cpp.c:73:\nstatic_assert_for_all_versions_of_c_and_cpp.c:193:5: error: expected specifier-qualifier-list before \xe2\x80\x98extern\xe2\x80\x99\n 193 | _Static_assert(2 > 1, "this should pass");\n | ^~~~~~~~~~~~~~\nRun Code Online (Sandbox Code Playgroud)\n我用来帮助我构建这个的参考资料位于上面源代码的注释中。以下是从那里复制的可点击链接:
\n_Pragma():如何禁用几行代码的 GCC 警告typedef char这个数组 hack 作为结构的灵感\n定义:\n\nC++03 示例静态断言错误输出:
\nSTATIC_ASSERT在 C++11 之前的用例中使用上述内容,下面是一些带有静态断言的示例代码,该断言预计会失败,因为它是 false:
#define CONCAT_(prefix, suffix) prefix##suffix\n#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)\n#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)\n\n/* Static assert hack required for **pre-C++11**, such as C++98, C++03, etc. */\n/* - It works only with C++, NOT with C! */\n#define _Static_assert_hack(expression, message) \\\n struct MAKE_UNIQUE_VARIABLE_NAME(static_assertion_failed) \\\n { \\\n _Pragma("GCC diagnostic push") \\\n _Pragma("GCC diagnostic ignored \\"-Wunused-local-typedefs\\"") \\\n typedef char static_assertion_failed[(expression) ? 1 : -1]; \\\n _Pragma("GCC diagnostic pop") \\\n }\nRun Code Online (Sandbox Code Playgroud)\n这是构建命令和失败输出的样子。对于 C++ 中的一个失败的静态断言来说,这是一大堆错误,但这对于像这样的 hack 来说是可以预料的,而_Static_assert上面介绍的 gcc C90 hack for 也好不到哪儿去:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++03 static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a\nstatic_assert_for_all_versions_of_c_and_cpp.c:129:67: error: narrowing conversion of \xe2\x80\x98-1\xe2\x80\x99 from \xe2\x80\x98int\xe2\x80\x99 to \xe2\x80\x98long unsigned int\xe2\x80\x99 is ill-formed in C++11 [-Werror=narrowing]\n 129 | typedef char static_assertion_failed[(expression) ? 1 : -1]; \\\n | ^\nstatic_assert_for_all_versions_of_c_and_cpp.c:139:36: note: in expansion of macro \xe2\x80\x98_Static_assert_hack\xe2\x80\x99\n 139 | #define _Static_assert _Static_assert_hack\n | ^~~~~~~~~~~~~~~~~~~\nstatic_assert_for_all_versions_of_c_and_cpp.c:151:5: note: in expansion of macro \xe2\x80\x98_Static_assert\xe2\x80\x99\n 151 | _Static_assert((test_for_true), "(" #test_for_true ") failed")\n | ^~~~~~~~~~~~~~\nstatic_assert_for_all_versions_of_c_and_cpp.c:187:5: note: in expansion of macro \xe2\x80\x98STATIC_ASSERT\xe2\x80\x99\n 187 | STATIC_ASSERT(2 > 2);\n | ^~~~~~~~~~~~~\nstatic_assert_for_all_versions_of_c_and_cpp.c:129:59: error: size \xe2\x80\x98-1\xe2\x80\x99 of array \xe2\x80\x98static_assertion_failed\xe2\x80\x99 is negative\n 129 | typedef char static_assertion_failed[(expression) ? 1 : -1]; \\\n | ~~~~~~~~~~~~~^~~~~~~~\nstatic_assert_for_all_versions_of_c_and_cpp.c:139:36: note: in expansion of macro \xe2\x80\x98_Static_assert_hack\xe2\x80\x99\n 139 | #define _Static_assert _Static_assert_hack\n | ^~~~~~~~~~~~~~~~~~~\nstatic_assert_for_all_versions_of_c_and_cpp.c:151:5: note: in expansion of macro \xe2\x80\x98_Static_assert\xe2\x80\x99\n 151 | _Static_assert((test_for_true), "(" #test_for_true ") failed")\n | ^~~~~~~~~~~~~~\nstatic_assert_for_all_versions_of_c_and_cpp.c:187:5: note: in expansion of macro \xe2\x80\x98STATIC_ASSERT\xe2\x80\x99\n 187 | STATIC_ASSERT(2 > 2);\n | ^~~~~~~~~~~~~\ncc1plus: all warnings being treated as errors\n\nRun Code Online (Sandbox Code Playgroud)\n请参阅static_assert_for_all_versions_of_c_and_cpp.c。
\nSTATIC_ASSERT(test_for_true)我在上面展示的最终宏(可处理所有版本的 C 和 C++)已在 Linux Ubuntu 20.04 上使用 gcc 编译器版本 ( gcc --version) 9.4.0( gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0) 进行了测试。
以下是它经过测试和工作的各种构建命令和语言。同样,在所有这些版本中,唯一不允许在结构和联合内部使用宏的版本是和!所有其他选项都支持在您想要的任何地方使用,包括函数外部、函数内部以及结构和联合内部。STATIC_ASSERT()-std=c90-std=c99STATIC_ASSERT
# -------------------\n# 1. In C:\n# --------
bob*_*ogo 11
我知道这个问题明确提到了gcc,但为了完整性,这里是微软编译器的一个调整.
使用负大小的数组typedef并不能说服cl吐出一个不错的错误.它只是说error C2118: negative subscript.在这方面,零宽度位域的表现更好.由于这涉及对struct进行类型化,我们确实需要使用唯一的类型名称.__LINE__没有削减芥末 - 可能COMPILE_TIME_ASSERT()在标题和源文件中有一个相同的行,你的编译将会中断.__COUNTER__来救援(自4.3以来它一直在gcc).
#define CTASTR2(pre,post) pre ## post
#define CTASTR(pre,post) CTASTR2(pre,post)
#define STATIC_ASSERT(cond,msg) \
typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
CTASTR(static_assertion_failed_,__COUNTER__)
Run Code Online (Sandbox Code Playgroud)
现在
STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)
Run Code Online (Sandbox Code Playgroud)
下cl得到:
错误C2149:'static_assertion_failed_use_another_compiler_luke':命名位字段的宽度不能为零
Gcc还提供了一个可理解的消息:
错误:位字段的零宽度'static_assertion_failed_use_another_compiler_luke'
| 归档时间: |
|
| 查看次数: |
63552 次 |
| 最近记录: |