특히 GCC를 강조하면서 C (C ++ 아님)에서 정적 어설 션 컴파일 시간을 달성하는 가장 좋은 방법은 무엇입니까?
답변:
C11 표준은 _Static_assert
키워드를 추가합니다 .
이것은 gcc-4.6 이후 로 구현되었습니다 .
_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */
첫 번째 슬롯은 정수 상수 표현식이어야합니다. 두 번째 슬롯은 긴 ( _Static_assert(0, L"assertion of doom!")
) 일 수있는 상수 문자열 리터럴입니다 .
이것은 최신 버전의 clang에서도 구현된다는 점에 유의해야합니다.
error: expected declaration specifiers or '...' before 'sizeof'
줄을 얻기 때문에 static_assert( sizeof(int) == sizeof(long int), "Error!);
(나는 C ++이 아닌 C를 사용하고 있습니다)
_Static_assert( sizeof(int) == sizeof(long int), "Error!");
내 macine에서 오류가 발생합니다.
error: expected declaration specifiers or '...' before 'sizeof'
AND error: expected declaration specifiers or '...' before string constant
(그는 "Error!"
문자열을 참조하고 있습니다 ) (또한 : -std = c11로 컴파일하고 있습니다. 함수 내부에 선언을 넣으면 모두 잘 작동합니다 (예상대로 실패 및 성공))
_Static_assert
C ++ ish가 아닌 C 표준을 사용했습니다 static_assert
. static_assert 매크로를 얻으려면`#include <assert.h>가 필요합니다.
이것은 함수 및 비 함수 범위에서 작동합니다 (구조체, 조합 내부에서는 작동하지 않음).
#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
-Og
)은 이것이 작동하기에 충분할 수 있지만 디버깅을 방해해서는 안됩니다. __OPTIMIZE__
(and __GNUC__
)가 정의되지 않은 경우 정적 assert를 no-op 또는 런타임 assert로 만드는 것을 고려할 수 있습니다 .
__LINE__
gcc 4.1.1 의 버전 과 비슷한 것을 사용합니다 ... 두 개의 다른 헤더가 같은 번호가 매겨진 줄에 하나가있을 때 가끔 짜증이납니다!
질문에 gcc가 명시 적으로 언급되어 있다는 것을 알고 있지만 여기에서 완전성을 위해 Microsoft 컴파일러에 대한 조정이 있습니다.
음수 크기의 배열 typedef를 사용하면 cl 이 괜찮은 오류를 내도록 설득하지 못합니다 . 그것은 단지 말한다 error C2118: negative subscript
. 이 점에서 너비가 0 인 비트 필드가 더 좋습니다. 여기에는 구조체 형식화가 포함되므로 고유 한 형식 이름을 사용해야합니다. __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': 명명 된 비트 필드의 너비는 0 일 수 없습니다.
Gcc는 또한 이해하기 쉬운 메시지를 제공합니다.
오류 : 비트 필드 'static_assertion_failed_use_another_compiler_luke'의 너비가 0입니다.
에서 위키 백과 :
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
다음을 사용하여 솔루션을 사용 하지 않는 것이 좋습니다 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
키워드 컴파일 타임에 배열이 정의됩니다. 이 assert는 COND
컴파일 타임에 평가되는 경우 에만 작동 합니다. 변수에 할당 된 값과 같이 메모리의 값을 기반으로하는 조건에서는 작동하지 않습니다 (즉, 컴파일이 실패합니다).
와 함께 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]
고전적인 방법은 배열을 사용하는 것입니다.
char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];
어설 션이 true이면 배열의 크기가 1이고 유효하기 때문에 작동하지만 false이면 크기 -1은 컴파일 오류를 제공합니다.
대부분의 컴파일러는 변수의 이름을 표시하고 어설 션에 대한 최종 주석을 남길 수있는 코드의 오른쪽 부분을 가리 킵니다.
#define STATIC_ASSERT()
타입 매크로 로 래핑 하고 제네릭 예제에서 더 많은 일반 예제와 샘플 컴파일러 출력을 STATIC_ASSERT()
제공하면 훨씬 더 많은 업 보트를 얻을 수 있고이 기술이 더 합리적이라고 생각합니다.
Perl에서 특히 perl.h
3455 행 ( <assert.h>
사전에 포함됨) :
/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
time invariants. That is, their argument must be a constant expression that
can be verified by the compiler. This expression can contain anything that's
known to the compiler, e.g. #define constants, enums, or sizeof (...). If
the expression evaluates to 0, compilation fails.
Because they generate no runtime code (i.e. their use is "free"), they're
always active, even under non-DEBUGGING builds.
STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
file scope (outside of any function).
STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
function.
*/
#if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
/* static_assert is a macro defined in <assert.h> in C11 or a compiler
builtin in C++11. But IBM XL C V11 does not support _Static_assert, no
matter what <assert.h> says.
*/
# define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
'typedef char x[n]' where n is not a compile-time constant.
We want to enforce constantness.
*/
# define STATIC_ASSERT_2(COND, SUFFIX) \
typedef struct { \
unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
} _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
# define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
# define STATIC_ASSERT_DECL(COND) STATIC_ASSERT_1(COND, __LINE__)
#endif
/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND) STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END
static_assert
가능한 경우 (에서<assert.h>
) 사용됩니다. 그렇지 않고 조건이 거짓이면 음수 크기의 비트 필드가 선언되어 컴파일이 실패합니다.
STMT_START
/ STMT_END
는 각각 do
/로 확장되는 매크로 while (0)
입니다.
_Static_assert()
이제 모든 버전의 C에 대해 gcc에서 정의됩니다. static_assert()
C ++ 11 이상에서 정의 됨STATIC_ASSERT()
작동합니다.g++ -std=c++11
) 이상gcc -std=c90
gcc -std=c99
gcc -std=c11
gcc
(지정된 표준 없음)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 : In function ' main'static_assert.c
: 78 : 38 : error : static assertion failed : "(1> 2) failed"
#define 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()
코드는 작동하지만 조건이 거짓이어서 컴파일 타임 어설 션이 발생 함) :
$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c : In function 'int main ()'
static_assert.c : 74 : 32 : error : static assertion failed : (1> 2) failed
#define _Static_assert static_assert / *static_assert
is part of C ++ 11 or later * /
^
static_assert.c : 78 : 38 : note : in expansion of macro '_Static_assert'#
define 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 ++ 버전을 사용하고 있기 때문에 assert 코드가 제대로 작동하지 않음 ) :
$ 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 : 'int main ()'함수에서
static_assert.c : 78 : 99 : 오류 : 'static_assert'가이 범위에서 선언되지 않았습니다.
#define 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. /programming/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. /programming/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;
}
static_assert
매크로 가 있는데 왜 그렇게 복잡 assert.h
합니까?
static_assert()
C에서는 전혀 사용할 수 없습니다. 여기도 참조하십시오 : en.cppreference.com/w/cpp/language/static_assert-static_assert
"(C ++ 11 이후)"존재를 보여줍니다 . 내 대답의 아름다움은 gcc의 C90 이상뿐만 아니라 C ++ 11 이상에서와 같이 C ++ 11 이상에서 작동하지 않는다는 것 static_assert()
입니다. 또한 내 대답에 대해 무엇이 복잡합니까? 그것은 단지 몇 #define
s입니다.
static_assert
C11 이후 C에서 정의됩니다. 로 확장되는 매크로입니다 _Static_assert
. en.cppreference.com/w/c/error/static_assert . 또한 귀하의 답변과는 대조적으로 _Static_assert
gcc의 c99 및 c90에서 사용할 수 없습니다 (gnu99 및 gnu90에서만). 이것은 표준을 준수합니다. 기본적으로 많은 추가 작업을 수행합니다. 이는 gnu90 및 gnu99로 컴파일 된 경우에만 이점을 제공하고 실제 사용 사례를 크게 작게 만듭니다.
정말 기본적이고 이식 가능한 것을 원하지만 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 를 가져 와서 자체적으로 나누어 주장 합니다. 이것은 두 가지 일을합니다.
그것이 0, id est, 어설 션이 실패하면 0으로 나누기 오류가 발생합니다 (배열을 선언하려고하기 때문에 산술이 강제 실행 됨).
0이 아니면 배열 크기를로 정규화합니다 1
. 따라서 어설 션이 통과되면 술어가 -1
(유효하지 않음) 또는 232442
(최적화되면 IDK로 막대한 공간 낭비 ) 평가 되었기 때문에 어쨌든 실패하는 것을 원하지 않을 것 입니다.
For 는 여러 번 쓸 수 있음을 의미합니다.
그것은 또한 그것을 캐스팅합니다STATIC_ASSERT
이 괄호로 싸여이 그것을 변수 스코프 블록을 만든다assert
void
, 이는 unused variable
경고를 제거하는 알려진 방법 입니다.
의 경우 GLOBAL_STATIC_ASSERT
코드 블록에있는 대신 네임 스페이스를 생성합니다. 함수 외부에서 네임 스페이스가 허용됩니다. unique
식별자는 당신이 한 번 이상이 하나를 사용하는 경우 충돌하는 정의를 중지해야합니다.
GCC 및 VS'12 C ++에서 나를 위해 일했습니다.
이것은 "사용하지 않는 제거"옵션 세트와 함께 작동합니다. 전역 매개 변수를 확인하기 위해 하나의 전역 함수를 사용할 수 있습니다.
//
#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
//
이것은 오래된 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
_Static_assert
.