컴파일 타임에 #define의 값을 어떻게 표시합니까?


124

내 코드가 사용하고 있다고 생각하는 Boost 버전을 파악하려고합니다. 다음과 같이하고 싶습니다.

#error BOOST_VERSION

그러나 전처리 기는 BOOST_VERSION을 확장하지 않습니다.

나는 프로그램에서 런타임에 그것을 인쇄 할 수 있다는 것을 알고 있고, 답을 찾기 위해 전 처리기의 출력을 볼 수 있다는 것을 알고 있습니다. 컴파일하는 동안 이것을하는 방법이 유용 할 수 있다고 생각합니다.


7
미래 방문자를 위해 ... Chris Barry는 마지막에 일반화 된 솔루션을 제공합니다 (Boost 특정 항목이 없음).
jww

답변:


118

이것은 원래 쿼리 이후 오랜 시간이라는 것을 알고 있지만 여전히 유용 할 수 있습니다.

이것은 stringify 연산자 "#"을 사용하여 GCC에서 수행 할 수 있지만 두 단계가 필요합니다.

#define XSTR(x) STR(x)
#define STR(x) #x

매크로 값은 다음과 같이 표시 될 수 있습니다.

#pragma message "The value of ABC: " XSTR(ABC)

참조 : 3.4 gcc 온라인 문서의 Stringification.

작동 원리 :

전처리 기는 인용 된 문자열을 이해하고 일반 텍스트와 다르게 처리합니다. 문자열 연결은 이러한 특수 처리의 예입니다. 메시지 pragma에는 인용 된 문자열 인 인수가 필요합니다. 인수에 둘 이상의 구성 요소가있는 경우 문자열 연결이 적용될 수 있도록 모두 문자열이어야합니다. 전처리 기는 인용되지 않은 문자열이 인용 된 것처럼 처리되어야한다고 가정 할 수 없습니다. 그렇다면 :

#define ABC 123
int n = ABC;

컴파일되지 않습니다.

이제 다음을 고려하십시오.

#define ABC abc
#pragma message "The value of ABC is: " ABC

이는

#pragma message "The value of ABC is: " abc

abc (따옴표 없음)를 앞의 문자열과 연결할 수 없기 때문에 전 처리기 경고가 발생합니다.

이제 전 처리기 스트링 화를 고려하십시오 (한때 스트링 화라고 불렸던 문서의 링크는 수정 된 용어를 반영하도록 변경되었습니다. 두 용어 모두 똑같이 혐오스러운 표현입니다. 물론 올바른 용어는 스트링 화입니다. 업데이트 할 준비를하십시오. 귀하의 링크.)) 연산자. 이것은 매크로의 인수에 대해서만 작동하며 확장되지 않은 인수를 큰 따옴표로 묶인 인수로 대체합니다. 그러므로:

#define STR(x) #x
char *s1 = "abc";
char *s2 = STR(abc);

s1과 s2에 동일한 값을 할당합니다. gcc -E를 실행하면 출력에서이를 볼 수 있습니다. 아마도 STR은 ENQUOTE와 같은 이름으로 더 잘 명명 될 것입니다.

이것은 인용되지 않은 항목 주위에 인용 부호를 넣는 문제를 해결합니다. 이제 문제는 인수가 매크로 인 경우 매크로가 확장되지 않는다는 것입니다. 이것이 두 번째 매크로가 필요한 이유입니다. XSTR은 인수를 확장 한 다음 STR을 호출하여 확장 된 값을 따옴표로 묶습니다.


3
왜 두 단계가 필요한지 궁금합니다
Vincent Fourmond

4
@VincentFourmond XSTR 단계가 없으면 매크로가 확장되지 않습니다. 따라서 #define ABC 42 \ n STR (ABC)를 수행하면 "ABC"가됩니다. gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c

이것은 또한 Xcode 8에서도 잘 작동합니다 __IPHONE_9_3. 예를 들어 ABC를 .
funroll 2010 년

GCC 용어는 지금의 URL, 변경하는 것, 그리고 그것으로 https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
크리스 베리

119

BOOST_PP_STRINGIZE C ++에는 훌륭한 솔루션이지만 일반 C에는 적합하지 않습니다.

다음은 GNU CPP에 대한 솔루션입니다.

/* Some test definition here */
#define DEFINED_BUT_NO_VALUE
#define DEFINED_INT 3
#define DEFINED_STR "ABC"

/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "="  VALUE(var)

/* Some example here */
#pragma message(VAR_NAME_VALUE(NOT_DEFINED))
#pragma message(VAR_NAME_VALUE(DEFINED_BUT_NO_VALUE))
#pragma message(VAR_NAME_VALUE(DEFINED_INT))
#pragma message(VAR_NAME_VALUE(DEFINED_STR))

위의 정의 결과 :

test.c:10:9: note: #pragma message: NOT_DEFINED=NOT_DEFINED
test.c:11:9: note: #pragma message: DEFINED_BUT_NO_VALUE=
test.c:12:9: note: #pragma message: DEFINED_INT=3
test.c:13:9: note: #pragma message: DEFINED_STR="ABC"

들어 "interger로 정의" , "문자열로 정의""정의되어 있지만 값" 변수는, 그들은 잘 작동하지 않습니다. "정의되지 않은" 변수에 대해서만 원래 변수 이름과 똑같이 표시됩니다. 익숙해야합니다. 그렇지 않으면 누군가가 더 나은 솔루션을 제공 할 수 있습니다.


우수한! ARM RVCT에 대한 경험이 있습니까? GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
문자열

2
훌륭한 솔루션입니다. 그러나 컴파일 시간 계산 된 값의 크기 (예 : 복잡한 구조체의 크기)를 표시하려면이 작업을 수행 할 수 있습니까? 이 답변에서 제안 된 방법 DEFINED_INT=(sizeof(MY_STRUCT))sizeof연산자를 평가 하지 않고 생성하는 것 같습니다 .
Carl

(의견 추가 : 예상치 못한 것은 아닙니다.을 평가하는 것은 전처리 기가 아니라 컴파일러이기 때문입니다. sizeof그러나 이것을 달성하는 영리한 방법이 있는지 여전히 궁금합니다.)
Carl

@xdan 좋은 솔루션, 불행하게도 같은 것들에 대한 수용하지 않고있다#define masks {0xff, 0xaf, 0x0f}
사이먼 베이글

59

Visual C ++를 사용하는 경우 다음을 사용할 수 있습니다 #pragma message.

#include <boost/preprocessor/stringize.hpp>
#pragma message("BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION))

편집 : 링크에 대한 LB 감사합니다

분명히 GCC에 해당하는 것은 (테스트되지 않음) :

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)

5
이를 진단 pragma라고합니다. gcc.gnu.org/onlinedocs/gcc/…
LB40

4
멋지고 짧고 복사 / 붙여 넣기 가능한 정의BOOST_PP_STRINGIZE 를 포함 하면 좋을 것입니다.
Timmmm

: GCC에서 잘 작동
토마스 LEGRIS

14

내가 아는 한 '#error'는 문자열 만 인쇄 하므로 실제로 따옴표를 사용할 필요도 없습니다 .

"BOOST_VERSION"을 사용하여 의도적으로 잘못된 코드를 다양하게 작성해 보셨습니까? 아마도 "blah [BOOST_VERSION] = foo;" "문자열 리터럴 1.2.1은 배열 주소로 사용할 수 없습니다"와 같은 메시지가 표시됩니다. 예쁜 오류 메시지는 아니지만 적어도 관련 값을 보여줄 것입니다. 값을 알려주는 컴파일 오류를 찾을 때까지 놀 수 있습니다.


BOOST_VERSION이 정수이기 때문에 작동하지 않았지만 다음 명령문으로 확인했습니다 : std::vector<BOOST_VERSION>;gcc 4.4.1에서. 감사!
Jim Hunziker

Visual C ++에서는 Bojan Resnik의 대답을 사용해야합니다.
Raphaël Saint-Pierre

나는 이것을 작동 시키려고 노력했지만 GCC가 내게 준 오류 메시지는 슬프게도 설명이 없었다. 그러나 그것을 언급하면 ​​+1.
Chris Lutz

14

부스트없이 :

  1. 동일한 매크로를 다시 정의하면 컴파일러 HIMSELF가 경고를 제공합니다.

  2. 경고에서 이전 정의의 위치를 ​​볼 수 있습니다.

  3. 이전 정의의 vi 파일.

ambarish@axiom:~/cpp$ g++ shiftOper.cpp
shiftOper.cpp:7:1: warning: "LINUX_VERSION_CODE" redefined
shiftOper.cpp:6:1: warning: this is the location of the previous definition

#define LINUX_VERSION_CODE 265216
#define LINUX_VERSION_CODE 666

int main ()
{

}

이것은 더 쉽고 간단합니다.
Tmx

1
자체 : 컴파일러는 성별이 없다
스카이

.NET과 같은 미리 정의 된 매크로에서는 작동하지 않습니다 __cplusplus.
ManuelAtWork

10

Microsoft C / C ++에서는 기본 제공을 사용하여 _CRT_STRINGIZE()상수를 인쇄 할 수 있습니다 . 내 stdafx.h파일 중에는 다음과 같은 조합이 포함되어 있습니다.

#pragma message("_MSC_VER      is " _CRT_STRINGIZE(_MSC_VER))
#pragma message("_MFC_VER      is " _CRT_STRINGIZE(_MFC_VER))
#pragma message("_ATL_VER      is " _CRT_STRINGIZE(_ATL_VER))
#pragma message("WINVER        is " _CRT_STRINGIZE(WINVER))
#pragma message("_WIN32_WINNT  is " _CRT_STRINGIZE(_WIN32_WINNT))
#pragma message("_WIN32_IE     is " _CRT_STRINGIZE(_WIN32_IE))
#pragma message("NTDDI_VERSION is " _CRT_STRINGIZE(NTDDI_VERSION)) 

다음과 같이 출력됩니다.

_MSC_VER      is 1915
_MFC_VER      is 0x0E00
_ATL_VER      is 0x0E00
WINVER        is 0x0600
_WIN32_WINNT  is 0x0600
_WIN32_IE     is 0x0700
NTDDI_VERSION is 0x06000000

5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : 치명적인 오류 C1083 : 포함 파일을 열 수 없음 : ':: 106200': 해당 파일 또는 디렉터리가 없습니다.

preprocess to file유효하지 않은 토큰이있는 경우에도이 활성화 된 경우에도 작동합니다 .

#define a <::'*/`#>
#include a
MSVC2015 : 치명적인 오류 C1083 : 포함 파일을 열 수 없음 : '::'* /`# ': 해당 파일 또는 디렉터리가
없습니다 . GCC4.x : 경고 :'문자 [-Winvalid-pp-token]
#define a <:: '* /`#>

내 말은 Build error: #include expects "FILENAME" or <FILENAME>. 한숨.
endolith

@endolith 어떤 컴파일러와 버전?
Andry 2011

DP8051 Keil 9.51 :)
endolith

: @endolith이 컴파일러는 매우 전처리에 제한됩니다 보인다 keil.com/support/man/docs/c51/c51_pp_directives.htm 내 예상대로 거의, 난 그냥 같은 잘못된 문자의 일부를 삭제 한 작품 옆에, 그러나 ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry를

감사합니다. pragma 메시지 항목이 내가 사용중인 컴파일러에서 구현되지 않았기 때문에 저를 구했습니다.
CodeMonkey

3

소스 파일을 전처리하고 전 처리기 값이 평가되는 것을 볼 수도 있습니다.


2

를 찾습니까

#if BOOST_VERSION != "1.2"
#error "Bad version"
#endif

내가 가정 한 것처럼 BOOST_VERSION이 문자열이면 좋지 않지만 메이저, 마이너 및 개정 번호에 대해 정의 된 개별 정수가있을 수도 있습니다.


제출자는 특정 값을 (단지) 강요하는 것이 아니라 현재 값이 무엇인지보고 싶어합니다.
KeyserSoze

이것은 나를 위해 일하는 유일한 것입니다. 나는 변경할 수 있습니다 #if VARIABLE == 123... 즉시 문을과 구문 강조는 내가 그것을인지 생각 값이인지 알려줍니다
endolith

2

전 처리기의 출력을 보는 것이 당신이 요구하는 대답에 가장 가까운 것입니다.

나는 당신이 그것을 (그리고 다른 방법으로) 배제했다는 것을 알고 있지만 그 이유는 확실하지 않습니다. 해결하기에 충분한 구체적인 문제가 있지만 "정상적인"방법 중 하나가 잘 작동하지 않는 이유를 설명하지 않았습니다.


이것은 아마도 일반적인 문제에 대한 정답 일 것입니다.
jww

1

인쇄 BOOST_VERSION하고 컴파일하고 빌드 시스템의 일부로 실행 하는 프로그램을 작성할 수 있습니다. 그렇지 않으면 운이 좋지 않은 것 같습니다.


헤더에 정의 된 소프트웨어 버전의 경우 아마도 안전 할 것입니다 (좋은 대답입니다). 그러나 일반적인 솔루션으로서 가능한 단점은 테스트 앱과 실제 앱이 동일한 #define 값을 갖도록하는 것입니다. 포함 경로에 따라 해당 값을 설정하는 데 사용될 수있는 다른 #defines 의 CFLAGS 등 컴파일러에 전달
KeyserSoze

실제 프로그램에서 인쇄하십시오. 그래픽 인 경우 "정보"대화 상자에 넣으십시오. 명령 줄인 경우 옵션으로 만듭니다 (--version의 일부일 수도 있음). 데몬 인 경우 로그 파일에 기록합니다. 포함 된 경우 다른 방법을 찾으십시오.
divegeek

@swillden-OP는 런타임이 아닌 컴파일 타임에 원했습니다.
Chris Lutz

이것은 또한 기반 크로스 컴파일러를 파괴하는 경향이 빌드
크레이그 벨소리를



0

#error 대신 매크로를 사용하기 직전에 다시 정의하십시오. 컴파일이 실패하고 컴파일러는 매크로에 적용되는 것으로 생각되는 현재 값을 제공합니다.

#define BOOST_VERSION blah

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.