Replacing text macros
전처리기는 텍스트 매크로 치환을 지원합니다. 함수 형태의 텍스트 매크로 치환도 지원됩니다.
목차 |
구문
#define
식별자 대체-목록
(선택적)
|
(1) | ||||||||
#define
식별자
(
매개변수들
)
대체-목록
(선택적)
|
(2) | ||||||||
#define
식별자
(
매개변수들
, ...)
대체-목록
(선택적)
|
(3) | (C++11 이후) | |||||||
#define
식별자
(...)
대체-목록
(선택적)
|
(4) | (C++11 이후) | |||||||
#undef
식별자
|
(5) | ||||||||
설명
#define 지시문
#define 지시문은 identifier 를 매크로로 정의하며, 이는 컴파일러에게 identifier 의 대부분의 연속적인 발생을 추가로 처리될 replacement-list 로 대체하도록 지시합니다. 예외는 스캐닝 및 대체 규칙 에서 발생합니다. 식별자가 이미 어떤 유형의 매크로로 정의되어 있는 경우, 정의가 동일하지 않으면 프로그램은 잘못된 형식입니다.
객체형 매크로
객체형 매크로는 정의된 identifier 의 모든 발생을 replacement-list 로 대체합니다. #define 지시자의 버전 (1)은 정확히 이런 방식으로 동작합니다.
함수형 매크로
함수형 매크로는 정의된 identifier 의 각 발생을 replacement-list 로 대체하며, 추가적으로 여러 인수를 취하여 replacement-list 내의 parameters 중 해당하는 발생을 대체합니다.
함수형 매크로 호출의 구문은 함수 호출의 구문과 유사합니다: 매크로 이름의 각 인스턴스 뒤에 ( 가 다음 전처리 토큰으로 오는 경우, 이는 replacement-list 로 대체되는 토큰 시퀀스를 시작합니다. 이 시퀀스는 일치하는 ) 토큰에 의해 종료되며, 중간에 있는 일치하는 왼쪽 및 오른쪽 괄호 쌍은 건너뜁니다.
버전 (2)의 경우, 인수의 개수가 매크로 정의의 매개변수 개수와 동일해야 합니다. 버전 (3,4)의 경우, 인수의 개수가 매개변수 개수보다 적어서는 안 됩니다 (
not
(since C++20)
counting
...
). 그렇지 않으면 프로그램이 ill-formed입니다. 식별자가 함수 표기법이 아닌 경우, 즉 괄호가 뒤에 오지 않는 경우에는 전혀 치환되지 않습니다.
버전 (2)의 #define 지시자는 단순한 함수형 매크로를 정의합니다.
지시문의 버전 (3)은
#define
가변 인자를 갖는 함수형 매크로를 정의합니다. 추가 인자들(
가변 인자
)은
__VA_ARGS__
식별자를 사용하여 접근할 수 있으며, 이 식별자는 교체될 식별자와 함께 제공된 인자들로 대체됩니다.
지시문의 버전 (4)는
#define
함수형 매크로를 가변 인자 개수로 정의하지만, 일반 인자는 없습니다. 인자들(
가변 인자
라고 함)은
__VA_ARGS__
식별자로만 접근할 수 있으며, 이 식별자는 교체될 식별자와 함께 제공된 인자들로 대체됩니다.
|
버전 (3,4)의 경우,
replacement-list
는 토큰 시퀀스
#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) F(a, b, c) // replaced by f(0, a, b, c) F() // replaced by f(0) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) G(a, b, c) // replaced by f(0, a, b, c) G(a, ) // replaced by f(0, a) G(a) // replaced by f(0, a) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) SDEF(foo); // replaced by S foo; SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 }; |
(C++20부터) |
참고: 함수형 매크로의 인수에 일치하는 왼쪽 및 오른쪽 괄호 쌍으로 보호되지 않은 쉼표가 포함된 경우(가장 일반적으로 템플릿 인수 목록에서 발견됨, 예를 들어 assert ( std:: is_same_v < int , int > ) ; 또는 BOOST_FOREACH ( std:: pair < int , int > p, m ) ), 해당 쉼표는 매크로 인수 구분자로 해석되어 인수 개수 불일치로 인한 컴파일 오류가 발생합니다.
스캐닝 및 교체
- 스캔은 매크로가 대체된 내용을 추적합니다. 스캔이 그러한 매크로와 일치하는 텍스트를 발견하면 "무시할 대상"으로 표시합니다(모든 스캔이 이를 무시함). 이는 재귀를 방지합니다.
- 스캔이 함수형 매크로를 발견한 경우, 인수들은 replacement-list 내부에 배치되기 전에 스캔됩니다. 단 # 및 ## 연산자는 스캔 없이 인수를 취합니다.
- 매크로가 대체된 후, 결과 텍스트는 스캔됩니다.
참고로, 유사 재귀 매크로를 정의하는 것이 가능합니다:
#define EMPTY #define SCAN(x) x #define EXAMPLE_() EXAMPLE #define EXAMPLE(n) EXAMPLE_ EMPTY()(n-1) (n) EXAMPLE(5) SCAN(EXAMPLE(5))
출력:
EXAMPLE_ ()(5 -1) (5) EXAMPLE_ ()(5 -1 -1) (5 -1) (5)
예약된 매크로 이름
표준 라이브러리 헤더를 포함하는 번역 단위 는 #define 또는 #undef 를 사용하여 표준 라이브러리 헤더 에 선언된 이름들을 재정의하거나 해제해서는 안 됩니다.
표준 라이브러리의 어떤 부분을 사용하는 번역 단위는 다음과 어휘적으로 동일한 이름을 #define 또는 #undef 하는 것이 허용되지 않습니다:
|
(C++11부터) |
그렇지 않으면, 동작은 정의되지 않습니다.
# 및 ## 연산자
함수형 매크로에서, # 연산자가 replacement-list 내의 식별자 앞에 위치하면, 해당 식별자를 매개변수 치환 과정에 통과시킨 후 결과를 따옴표로 감싸서 문자열 리터럴을 효과적으로 생성합니다. 추가로, 전처리기는 포함된 문자열 리터럴 주변의 따옴표를 이스케이프하기 위해 백슬래시를 추가하고, 필요한 경우 문자열 내부의 백슬래시를 두 배로 늘립니다. 모든 앞뒤 공백은 제거되며, 텍스트 중간의 공백 시퀀스(그러나 포함된 문자열 리터럴 내부는 제외)는 단일 공백으로 축소됩니다. 이 연산을 "stringification"이라고 합니다. Stringification의 결과가 유효한 문자열 리터럴이 아닌 경우, 동작은 정의되지 않습니다.
|
#
가
#define showlist(...) puts(#__VA_ARGS__) showlist(); // expands to puts("") showlist(1, "x", int); // expands to puts("1, \"x\", int") |
(C++11부터) |
##
연산자는
replacement-list
내에서 연속된 두 식별자 사이에 위치할 때, 두 식별자(먼저 매크로 확장되지 않음)에 대해 매개변수 치환을 수행한 후 결과를 연결합니다. 이 연산을 "토큰 접합" 또는 "토큰 붙여넣기"라고 합니다. 함께 유효한 토큰을 형성하는 토큰만 접합할 수 있습니다: 더 긴 식별자를 형성하는 식별자, 숫자를 형성하는 숫자, 또는
+
와
=
연산자가 결합되어
+=
를 형성하는 경우 등입니다.
/
와
*
를 접합하여 주석을 생성할 수는 없습니다. 왜냐하면 주석은 매크로 치환이 고려되기 전에 텍스트에서 제거되기 때문입니다. 접합 결과가 유효한 토큰이 아닌 경우, 동작은 정의되지 않습니다.
참고: 일부 컴파일러는 쉼표 다음과
__VA_ARGS__
앞에
##
가 나타날 수 있도록 하는 확장 기능을 제공하는데, 이 경우 가변 인수가 있을 때는
##
이 아무 작업도 수행하지 않지만, 가변 인수가 없을 때는 쉼표를 제거합니다: 이를 통해
fprintf
(
stderr
, format,
##__VA_ARGS__)
와 같은 매크로를 정의할 수 있습니다.
이는 표준 방식으로
__VA_OPT__
를 사용하여
fprintf
(
stderr
, format __VA_OPT__
(
,
)
__VA_ARGS__
)
와 같이 구현할 수도 있습니다.
(C++20부터)
#undef 지시문
#undef 지시자는 식별자 를 정의 해제합니다. 즉, #define 지시자에 의한 식별자 의 이전 정의를 취소합니다. 만약 해당 식별자에 연결된 매크로가 없는 경우, 이 지시자는 무시됩니다.
미리 정의된 매크로
다음 매크로 이름들은 모든 번역 단위에서 미리 정의되어 있습니다:
|
__cplusplus
|
사용 중인 C++ 표준 버전을 나타내며, 값으로 확장됨
|
|
__STDC_HOSTED__
(C++11)
|
구현이 호스트된 경우(OS에서 실행) 정수 상수
1
으로 확장되고, 독립 실행형인 경우(OS 없이 실행)
0
으로 확장됨
(매크로 상수) |
|
__FILE__
|
현재 파일 이름을 문자 문자열 리터럴로 확장하며,
#line
지시문으로 변경 가능
(매크로 상수) |
|
__LINE__
|
현재
물리적 소스 줄
의 줄 번호를 정수 상수로 확장하며,
#line
지시문으로 변경 가능
(매크로 상수) |
|
__DATE__
|
변환 날짜를
"Mmm dd yyyy"
형태의 문자 문자열 리터럴로 확장함.
"dd"
의 첫 번째 문자는 날짜가 10보다 작을 경우 공백임. 월 이름은
std::
asctime
(
)
으로 생성된 것과 같음
(매크로 상수) |
|
__TIME__
|
변환 시간을
"hh:mm:ss"
형태의 문자 문자열 리터럴로 확장함
(매크로 상수) |
|
__STDCPP_DEFAULT_NEW_ALIGNMENT__
(C++17)
|
정렬 인식 없는
operator new
호출로 보장되는 정렬 값을 가지는
std::size_t
리터럴로 확장됨(더 큰 정렬은
operator new
(
std::
size_t
,
std::
align_val_t
)
와 같은 정렬 인식 오버로드에 전달됨)
(매크로 상수) |
|
__STDCPP_BFLOAT16_T__
__STDCPP_FLOAT16_T__
__STDCPP_FLOAT32_T__
__STDCPP_FLOAT64_T__
__STDCPP_FLOAT128_T__
(C++23)
|
구현이 해당
확장 부동소수점 타입
을 지원하는 경우에만
1
으로 확장됨
(매크로 상수) |
|
__STDC_EMBED_NOT_FOUND__
__STDC_EMBED_FOUND__
__STDC_EMBED_EMPTY__
(C++26)
|
각각
0
,
1
및
2
으로 확장됨
(매크로 상수) |
구현에 의해 다음과 같은 추가 매크로 이름이 미리 정의될 수 있습니다:
|
__STDC__
|
구현에서 정의된 값, 존재하는 경우 일반적으로 C 규준 준수를 나타내는 데 사용됨
(매크로 상수) |
||||
|
__STDC_VERSION__
(C++11)
|
구현에서 정의된 값, 존재하는 경우
(매크로 상수) |
||||
|
__STDC_ISO_10646__
(C++11)
|
(매크로 상수) |
||||
|
__STDC_MB_MIGHT_NEQ_WC__
(C++11)
|
1
로 확장됨 - 기본 문자 집합의 멤버
x
에 대해
'x'
==
L
'x'
가 거짓일 수 있는 경우 (예: 유니코드를 사용하는 EBCDIC 기반 시스템에서
wchar_t
를 사용할 때)
(매크로 상수) |
||||
|
__STDCPP_THREADS__
(C++11)
|
1
로 확장됨 - 프로그램이 둘 이상의 실행 스레드를 가질 수 있는 경우
(매크로 상수) |
|
__STDCPP_STRICT_POINTER_SAFETY__
(C++11)
(C++23에서 제거됨)
|
1
로 확장됨 - 구현체가 엄격한
std::pointer_safety
를 지원하는 경우
(매크로 상수) |
이러한 매크로들의 값(
__FILE__
과
__LINE__
제외)은 번역 단위 전체에서 일정하게 유지됩니다. 이러한 매크로들을 재정의하거나 정의 해제하려는 시도는 정의되지 않은 동작을 초래합니다.
언어 기능 테스트 매크로표준은 C++11 이상에서 도입된 C++ 언어 기능에 해당하는 사전 정의된 매크로 집합을 정의합니다. 이들은 해당 기능의 존재를 감지하는 간단하고 이식 가능한 방법으로 의도되었습니다. 자세한 내용은 기능 테스트 를 참조하십시오. |
(C++20 이후) |
참고 사항함수 지역 사전 정의 변수 __func__ 는 사전 정의 매크로가 아니지만, 일반적으로 __FILE__ 및 __LINE__ 와 함께 사용됩니다(예: assert 에서). |
(C++11부터) |
예제
#include <iostream> // 함수 팩토리 생성 및 사용 #define FUNCTION(name, a) int fun_##name() { return a; } FUNCTION(abcd, 12) FUNCTION(fff, 2) FUNCTION(qqq, 23) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) std::cout << "output: " #a << '\n' // 이후 매크로 정의에서 매크로 사용 #define WORD "Hello " #define OUTER(...) WORD #__VA_ARGS__ int main() { std::cout << "abcd: " << fun_abcd() << '\n'; std::cout << "fff: " << fun_fff() << '\n'; std::cout << "qqq: " << fun_qqq() << '\n'; std::cout << FUNCTION << '\n'; OUTPUT(million); //따옴표가 없는 것에 주목 std::cout << OUTER(World) << '\n'; std::cout << OUTER(WORD World) << '\n'; }
출력:
abcd: 12 fff: 2 qqq: 23 34 output: million Hello World Hello WORD World
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 2908 | C++98 |
__LINE__
이 현재 물리적 라인 번호로 확장되는지
논리적 라인 번호로 확장되는지 불분명했음 |
현재 물리적 라인 번호로
확장됨 |
| LWG 294 | C++98 |
표준 라이브러리 헤더를 포함하는 번역 단위가 다른 표준 라이브러리
헤더에 선언된 이름을 정의하는 매크로를 포함할 수 있었음 |
금지됨 |
| P2621R2 | C++23 |
유니버설 문자 이름을 토큰 연결으로
형성하는 것이 허용되지 않았음 |
허용됨 |
참고 항목
|
C++ 문서
for
매크로 심볼 인덱스
|
|
|
C 문서
for
텍스트 매크로 치환
|