C 컴파일러는 어떻게 구현합니까?
에러의 경우, 런타임에 실패하는 것이 아니라 컴파일을 방해하는 「어카운트」를 실장하고 싶습니다.
현재 이와 같이 정의된 것이 하나 있는데, 이는 매우 잘 작동하지만 바이너리의 크기가 커집니다.
#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는 "array foo size is negative"(어레이 foo size is negative)), 이 오류가 실제로 어설션 검사임을 암시하는 어레이 유형의 이름을 선택해야 합니다.
하나 해야 할 는, 하다, 하다, 하다, 라고 것 밖에 할 수 입니다.typedef
특정 유형 이름을 컴파일 단위로 한 번 지정합니다.따라서 매크로는 선언할 고유한 유형 이름을 얻기 위해 각 용도에 대해 정렬해야 합니다.
통상적인 해결책은 매크로에 2개의 파라미터가 있어야 한다는 것입니다.첫 번째 조건은 true이고 두 번째 조건은 뒤에서 선언된 유형 이름의 일부입니다.플린스별 답변은 토큰 페이스트와__LINE__
사전 정의된 매크로를 사용하여 별도의 인수가 필요 없는 고유한 이름을 형성할 수 있습니다.
안타깝게도 어설션체크가 포함된 파일에 포함되어 있는 경우에도 두 번째 포함된 파일의 동일한 행 번호 또는 메인 소스 파일의 해당 행 번호의 검사와 충돌할 수 있습니다.를 붙일 수 방법은 매크로를 .__FILE__
, 의 일부로 수 이 아닌 할 수 것도 없습니다.정규 파일명에 ID의 일부가 아닌 문자를 포함할 수 있는 것은 말할 것도 없습니다.
그래서 저는 다음과 같은 코드 조각을 제안합니다.
/** 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 데모cdemo.c:32: 오류: 어레이 'syslog_syslog_c_32' 크기가 음수입니다.$
와 같다COMPILER_VERIFY(exp)
매크로가 꽤 잘 작동합니다.
// 인수 결합(인수 확장 후)#글루 정의(a,b) __GLUE(a,b)#정의_GLUE(a,b)a ##b #정의 CVERIFY(expr, msg) typedef char GLUE(compiler_verify_, msg) [(expr)? (+1) : (-1)] #define 컴파일러_VERIFY(exp) CVERIFY(exp, __LINE__)
C와 C++ 모두에서 동작하며 typedef가 허용되는 모든 장소에서 사용할 수 있습니다.이 표현이 true일 경우 typedef가 1자 배열로 생성됩니다(이는 무해합니다).식이 false일 경우 -1 문자의 배열에 대해 typedef를 생성합니다.이 경우 일반적으로 오류 메시지가 나타납니다.argment로 지정된 표현식은 컴파일 시간 상수로 평가되는 모든 것이 될 수 있습니다(따라서 sizeof()를 포함하는 표현식은 정상적으로 작동합니다).이것에 의해, 보다 유연성이 향상됩니다.
#if (expr)#오류#엔디프
프리프로세서로 평가할 수 있는 표현으로 제한됩니다.
Leander가 말했듯이, 정적 어설션이 C++11에 추가되고 있으며, 현재는 추가되어 있습니다.
static_assert(exp, message)
예를들면
#include "myfile.hpp"
static_assert(sizeof(MyClass) == 16, "MyClass is not 16 bytes!")
void doStuff(MyClass object) { }
cpp reference 페이지를 참조해 주세요.
컴파일러가 DEBUG 나 NDEBUG 등의 프리프로세서 매크로를 설정하고 있는 경우는, 다음과 같이 할 수 있습니다(그 이외의 경우는, Make file로 설정할 수 있습니다).
#ifdef DEBUG
#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}
#else
#define MY_COMPILER_ASSERT(EXPRESSION)
#endif
그 후 컴파일러는 디버깅빌드에 대해서만 어설트합니다.
C의 정적 어설션에서 찾을 수 있는 최고의 기입은 픽셀 비트입니다.스태틱 어설션이 C++ 0X에 추가되어 C1X에 추가될 가능성이 있지만 당분간은 그렇지 않습니다.제가 제공한 링크의 매크로가 당신의 바이너리 크기를 늘릴 수 있을지 모르겠습니다.적어도 합리적인 수준의 최적화로 컴파일하면 그렇지 않을 것이라고 생각합니다만, 주행거리는 다를 수 있습니다.
#error'를 사용하는 것은 대부분의 컴파일러에서 컴파일을 정지시키는 유효한 프리프로세서 정의입니다.예를 들어 디버깅에서 컴파일을 방지하기 위해 다음과 같이 실행할 수 있습니다.
#ifdef DEBUG
#error Please don't compile now
#endif
C에 관심이 있으시겠지만, Boost의 C++ static_assert를 봐주세요.(공교롭게도 이것은 C++1x에서 이용하실 수 있을 것 같습니다.)
C++에 대해서도 비슷한 작업을 실시했습니다.
#compiler_ASSERT(expr) 열거 {ARG_JOIN(CompilerAssertAtLine, __LINE__) = sizeof( char[(expr) ? +1 : - 1 ] }
이것은 C++에서만 동작합니다.이 문서에서는 C에서 사용하기 위해 수정하는 방법에 대해 설명합니다.
최종 바이너리를 컴파일 할 때는, MY_COMPILER_ASSERT 를 공백으로 해, 그 출력이 결과에 포함되지 않게 합니다.디버깅에 사용하는 방식만 정의해 주세요.
하지만 이런 식으로 모든 주장을 이해할 수는 없을 겁니다.컴파일 시에 의미가 없는 것도 있습니다(값이 null이 아니라는 주장 등).다른 #defines 값만 확인할 수 있습니다.네가 왜 그렇게 하고 싶어 하는지 잘 모르겠어.
이것은 GCC의 에러 메시지를 가장 적게 혼란스럽게 하는 것을 알 수 있습니다.다른 모든 것들은 음의 크기나 다른 혼란스러운 것에 대한 접미사를 가지고 있었다.
#define STATIC_ASSERT(expr, msg) \
typedef char ______Assertion_Failed_____##msg[1]; __unused \
typedef char ______Assertion_Failed_____##msg[(expr)?1:2] __unused
사용 예:
unsigned char testvar;
STATIC_ASSERT(sizeof(testvar) >= 8, testvar_is_too_small);
또한 gcc의 오류 메시지(ARM/GNU C 컴파일러: 6.3.1):
conflicting types for '______Assertion_Failed_____testvar_is_too_small'
거기서 하는 일은 어레이를 정의하는 것입니다.
#define MY_COMPILER_ASSERT(EXPRESSION) char x[(EXPRESSION)];
EXPRESSION이 true일 경우 다음과 같이 정의됩니다.char x[1];
그래도 괜찮습니다.false일 경우,char x[0];
그건 불법이야
언급URL : https://stackoverflow.com/questions/807244/c-compiler-asserts-how-to-implement
'programing' 카테고리의 다른 글
매개 변수를 사용하여 Vue 라우터 변경 오류를 해결하는 방법 (0) | 2022.07.18 |
---|---|
봄철 Tomcat에서 제공하는 JNDI DataSource 사용방법 (0) | 2022.07.18 |
Vue는 사용하기 전에 소품에서 데이터를 기다립니다. (0) | 2022.07.18 |
휴지 상태에서의 지연 로딩이란 무엇입니까? (0) | 2022.07.18 |
OSGi는 무엇을 해결합니까? (0) | 2022.07.18 |