什么是实现编译时静态断言在C(不是C ++),特别强调GCC的最佳方式?
Answer 1:
C11标准增加了_Static_assert
关键字。
这是因为GCC-4.6实现的 :
_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */
第一时隙必须是一个积分常数表达式。 所述第二槽是一个常数字符串文字可以是长( _Static_assert(0, L"assertion of doom!")
我要指出,这也是在最近版本的铛的实现。
Answer 2:
这个工程在功能和非功能范围(而不是内部结构,联合)。
#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);
}
如果编译时断言不能匹配,然后被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]
这恰好甚至在生锈的奥尔德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);
}
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()
{
}
这是它的样子:
$ 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
Answer 3:
CL
我知道这个问题明确提到的gcc,但只是为了完整性这里是Microsoft编译一个好办法。
使用负大小的数组类型定义不劝CL吐出一个体面的错误。 它只是说, error C2118: negative subscript
。 零宽度位字段票价在这方面更好。 因为这涉及typedeffing一个结构,我们真的需要使用唯一的类型名称。 __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__)
现在
STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)
下cl
得出:
错误C2149:“static_assertion_failed_use_another_compiler_luke”:命名位字段不能具有零宽度
海湾合作委员会也给出了一个可理解的消息:
误差:零宽度位字段“static_assertion_failed_use_another_compiler_luke”
Answer 4:
从维基百科 :
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Answer 5:
如果使用与STATIC_ASSERT()宏__LINE__
,能够避免在.c文件的条目,并通过包括在头文件中不同的入口之间的行号的冲突__INCLUDE_LEVEL__
。
例如 :
/* Trickery to create a unique variable name */
#define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y )
#define BOOST_DO_JOIN( X, Y ) BOOST_DO_JOIN2( X, Y )
#define BOOST_DO_JOIN2( X, Y ) X##Y
#define STATIC_ASSERT(x) typedef char \
BOOST_JOIN( BOOST_JOIN(level_,__INCLUDE_LEVEL__), \
BOOST_JOIN(_assert_on_line_,__LINE__) ) [(x) ? 1 : -1]
Answer 6:
我不建议使用采用该解决方案typedef
:
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
与阵列声明typedef
关键字,不能保证在编译时进行评估。 例如,在块范围下面的代码将编译:
int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);
我反而建议内容(在C99):
#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]
由于的static
参数,则阵列将在编译时定义。 请注意,这个断言将只与工作COND
其在编译时进行评估。 它不会(即编译会失败)与基于存储器中的值,如分配给变量的值的条件下工作。
Answer 7:
经典方法是使用阵列:
char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];
它的工作原理,因为如果断言是真正的数组的大小为1,这是有效的,但如果是假-1的尺寸给出了一个编译错误。
大多数编译器将显示该变量并指向代码,您可以留下有关断言最终注释权部分的名称。
Answer 8:
因为:
-
_Static_assert()
现在在GCC定义对于C的所有版本,并 -
static_assert()
是在C ++ 11和后面定义
以下简单的宏STATIC_ASSERT()
因此工作在:
- C ++:
- C ++ 11(
g++ -std=c++11
)或更高
- C ++ 11(
- C:
-
gcc -std=c90
-
gcc -std=c99
-
gcc -std=c11
-
gcc
(未指定STD)
-
定义STATIC_ASSERT
如下:
/* For C++: */
#ifdef __cplusplus
#ifndef _Static_assert
#define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
#endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
现在使用它:
STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed"
例子:
使用gcc 4.8.4测试过在Ubuntu:
实施例1:良好gcc
输出(即: STATIC_ASSERT()
代码的工作,但条件是假的,引起一个编译时断言):
$ GCC -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c:在函数“主”
static_assert.c:78:38:错误:静态断言失败: “(1> 2)失败”
#定义STATIC_ASSERT(test_for_true)_Static_assert((test_for_true), “(” #test_for_true “)失败”)
^
static_assert.c:88:5:注:在宏 'STATIC_ASSERT' 的膨胀
STATIC_ASSERT(1> 2);
^
实施例2:好g++ -std=c++11
输出(即: STATIC_ASSERT()
代码的工作,但条件是假的,引起一个编译时断言):
$克++ -Wall -std = C ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c:在函数“诠释主()”
static_assert.c:74:32:错误:静态断言失败:(1> 2)失败
的#define _Static_assert static_assert / *static_assert
是C ++ 11或后面部分* /
^
static_assert.c:78:38:注意:在宏观扩张 '_Static_assert'
#定义STATIC_ASSERT(test_for_true)_Static_assert((test_for_true), “(” #test_for_true “)失败”)
^
static_assert.c:88:5:注:在宏 'STATIC_ASSERT' 的膨胀
STATIC_ASSERT(1> 2);
^
实施例3: 失败 C ++输出(即:断言码不能正常工作在所有,因为这是使用前 C ++ 11的一个版本的C ++):
$ G ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c:88:5:警告:标识符 'static_assert' 是在C ++ 11 [-Wc ++ 0x中-COMPAT]关键字
STATIC_ASSERT(1> 2);
^
static_assert.c:在函数“诠释主()”
static_assert.c:78:99:错误: 'static_assert' 在此范围未声明
#定义STATIC_ASSERT(test_for_true)_Static_assert((test_for_true), “(” #test_for_true “)失败”)
^
static_assert.c:88:5:注:在宏 'STATIC_ASSERT' 的膨胀
STATIC_ASSERT(1> 2);
^
全部测试结果在这里:
/*
static_assert.c
- test static asserts in C and C++ using gcc compiler
Gabriel Staples
4 Mar. 2019
To be posted in:
1. https://stackoverflow.com/questions/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. https://stackoverflow.com/questions/3385515/static-assert-in-c/7287341#7287341
To compile & run:
C:
gcc -Wall -o static_assert static_assert.c && ./static_assert
gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert
gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert
gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert
C++:
g++ -Wall -o static_assert static_assert.c && ./static_assert
g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert
g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert
g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert
-------------
TEST RESULTS:
-------------
1. `_Static_assert(false, "1. that was false");` works in:
C:
gcc -Wall -o static_assert static_assert.c && ./static_assert YES
gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert YES
gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert YES
gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert YES
C++:
g++ -Wall -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert NO
2. `static_assert(false, "2. that was false");` works in:
C:
gcc -Wall -o static_assert static_assert.c && ./static_assert NO
gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert NO
gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert NO
gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert NO
C++:
g++ -Wall -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert YES
3. `STATIC_ASSERT(1 > 2);` works in:
C:
gcc -Wall -o static_assert static_assert.c && ./static_assert YES
gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert YES
gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert YES
gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert YES
C++:
g++ -Wall -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert NO
g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert YES
*/
#include <stdio.h>
#include <stdbool.h>
/* For C++: */
#ifdef __cplusplus
#ifndef _Static_assert
#define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
#endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")
int main(void)
{
printf("Hello World\n");
/*_Static_assert(false, "1. that was false");*/
/*static_assert(false, "2. that was false");*/
STATIC_ASSERT(1 > 2);
return 0;
}
Answer 9:
对于那些你想要的东西非常基本和便携,但没有进入C ++ 11的特点,我刚写的东西。
使用STATIC_ASSERT
正常(你可以在同一个函数写两次,如果你想),并使用GLOBAL_STATIC_ASSERT
的功能之外具有独特的短语作为第一个参数。
#if defined(static_assert)
# define STATIC_ASSERT static_assert
# define GLOBAL_STATIC_ASSERT(a, b, c) static_assert(b, c)
#else
# define STATIC_ASSERT(pred, explanation); {char assert[1/(pred)];(void)assert;}
# define GLOBAL_STATIC_ASSERT(unique, pred, explanation); namespace ASSERTATION {char unique[1/(pred)];}
#endif
GLOBAL_STATIC_ASSERT(first, 1, "Hi");
GLOBAL_STATIC_ASSERT(second, 1, "Hi");
int main(int c, char** v) {
(void)c; (void)v;
STATIC_ASSERT(1 > 0, "yo");
STATIC_ASSERT(1 > 0, "yo");
// STATIC_ASSERT(1 > 2, "yo"); //would compile until you uncomment this one
return 0;
}
说明:
首先,如果你有真正的断言,你就一定要使用,如果它是可用它检查。
如果不这样做就断言通过让您的pred
icate,并自行将它。 这做了两两件事。
如果它是零,ID EST,断言失败,它会零错误导致除(算术被迫,因为它试图声明数组)。
如果不是零,它正常化数组大小到1
。 因此,如果断言过去了,你不希望因为你的谓语评估,它终将以失败告终-1
(无效),或者是232442
(空间大量浪费,IDK它是否会被优化掉了)。
对于STATIC_ASSERT
它被包裹在括号,这使得它的模块,其作用域变量assert
,这意味着你可以写了很多次。
它也蒙上它void
,这是摆脱已知的方式unused variable
警告。
对于GLOBAL_STATIC_ASSERT
,而不是在一个码块时,它产生一个名称空间。 命名空间允许的功能外。 一个unique
的标识符需要停止任何冲突的定义,如果你使用这一个不止一次。
工作对我来说在GCC和VS'12 C ++
Answer 10:
这工作,用“删除未使用的”选项集。 我可以用一个全局函数来检查的全局参数。
//
#ifndef __sassert_h__
#define __sassert_h__
#define _cat(x, y) x##y
#define _sassert(exp, ln) \
extern void _cat(ASSERT_WARNING_, ln)(void); \
if(!(exp)) \
{ \
_cat(ASSERT_WARNING_, ln)(); \
}
#define sassert(exp) _sassert(exp, __LINE__)
#endif //__sassert_h__
//-----------------------------------------
static bool tab_req_set_relay(char *p_packet)
{
sassert(TXB_TX_PKT_SIZE < 3000000);
sassert(TXB_TX_PKT_SIZE >= 3000000);
...
}
//-----------------------------------------
Building target: ntank_app.elf
Invoking: Cross ARM C Linker
arm-none-eabi-gcc ...
../Sources/host_if/tab_if.c:637: undefined reference to `ASSERT_WARNING_637'
collect2: error: ld returned 1 exit status
make: *** [ntank_app.elf] Error 1
//
Answer 11:
这个工作对一些旧的gcc。 对不起,我忘了是什么版本:
#define _cat(x, y) x##y
#define _sassert(exp, ln)\
extern char _cat(SASSERT_, ln)[1]; \
extern char _cat(SASSERT_, ln)[exp ? 1 : 2]
#define sassert(exp) _sassert((exp), __LINE__)
//
sassert(1 == 2);
//
#148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134) main.c /test/source/controller line 134 C/C++ Problem