Namespaces
Variants

Replacing text macros

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

전처리기는 텍스트 매크로 치환을 지원합니다. 함수 형태의 텍스트 매크로 치환도 지원됩니다.

목차

구문

#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 는 토큰 시퀀스 __VA_OPT__( content  ) 를 포함할 수 있으며, 이는 __VA_ARGS__ 가 비어 있지 않으면 content 로 대체되고, 그렇지 않으면 아무것도 확장되지 않습니다.

#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의 결과가 유효한 문자열 리터럴이 아닌 경우, 동작은 정의되지 않습니다.

# __VA_ARGS__ 앞에 나타날 때, 전체 확장된 __VA_ARGS__ 가 따옴표로 둘러싸입니다:

#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++ 표준 버전을 나타내며, 값으로 확장됨
  • 199711L (C++11 이전) ,
  • 201103L (C++11) ,
  • 201402L (C++14) ,
  • 201703L (C++17) ,
  • 202002L (C++20) , 또는
  • 202302L (C++23)
    (매크로 상수)
__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)

yyyymmL 형식의 정수 상수로 확장됨, wchar_t 가 유니코드를 사용하는 경우, 이 날짜는 지원되는 유니코드의 최신 개정판을 나타냄

(C++23까지)

구현에서 정의된 값, 존재하는 경우

(C++23부터)

(매크로 상수)
__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 텍스트 매크로 치환