cpp preprocessor (전처리기)
전처리기는 소스코드를 컴파일 전단계 (translation phase)에서 처리된다. (런타임이 아니다.) 전처리가 된 후 컴파일러로 전달된다.
+지시어
#으로 시작한다.
키워드로는 define, undef, include, if, ifdef, ifndef, else, elif, endif, line, error, pragma 가 있다.
null 지시어로 #만 쓰는 것도 가능하다. (아무일도 안 함.)
+기능
전처리기는 소스 파일의 번역을 한다.
소스의 일부를 조건에 따라 컴파일할 수 있다. (#if, #ifdef, #ifndef, #else, #elif, #endif)
텍스트를 변경하는 매크로 기능을 한다. (텍스트를 붙이거나 식별자를 스트링으로 사용하는 등) (#define, #undef, 오퍼레이터로써 # , ## )
다른 파일들을 포함시킬 수 있다. (#include)
에러를 발생시킬 수 있다. (#error)
파일명과 라인 번호 정보를 알 수 있다. ( #line )
지정된 방식으로 작업을 한다. (#pragma)
+조건적 포함
#if [expression]
#ifdef [identifier]
#ifndef [identifier]
#elif [expression]
#else
#endif
조건에 따라 코드 블록이 수행된다.
#if, #elif 뒤에 나오는 expression은 상수적 표현을 의미한다. unary 연산자를 포함할 수 있고, defined identifier 나 defined (identifier) 를 사용할 수 있다.
expression의 값이 non-zero이면 해당 블록이 처리되고, 0이면 스킵된다.
#ifdef, #ifndef는 다음과 같다. (identifer가 매크로명으로 정의되었는지 검사한다.)
#if defined identifier
#if !defined identifier
#define ABCD 2
#include <iostream>
int main()
{
#ifdef ABCD
std::cout << "1: yes\n" ;
#else
std::cout << "1:no\n" ;
#endif
#ifndef ABCD
std::cout << "2:no1\n" ;
#elif ABCD==2
std::cout << "2.yes\n" ;
#else
std::cout << "2.no2\n" ;
#endif
#if !defined(DCCA) && (ABCD < 2*4-3)
std::cout << "3. yes\n" ;
#endif
}
모두 yes가 출력됨.
+텍스트 변경 매크로
#define identifier replacement-list
#define identifier(parameters) replacement-list
#define identifier(parameters, ...) replacement-list (C++11)
#define identifier(...) replacement-list (C++11)
#undef identifier
#define은 지정한 식별자를 replacement-list로 변경해 준다.
객체같은 매크로 ; identifier를 replacement-list로 교체한다.
함수같은 매크로 ; 위와 같지만 추가적으로 파라미터를 사용한다.
variable arguments의 경우 __VA_ARGS__ identifier를 사용한다.
__VA_OPT__(content)는 __VA_ARGS__가 비어 있지 않을 경우 확장된다.
#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)
F(1,2,3) --> f(0, 1,2,3)
F() --> f(0)
#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__ )
G(a,b,c) --> f(0, a,b,c)
G(a,) --> f(0, a)
G(a) --> f(0, a)
#define SDEF(sname, ...) S sname __VA_OPT__(= {__VA_ARGS__} )
SDEF(foo); --> S foo;
SDEF(x, 1,2,3); --> S x = {1,2,3};
오퍼레이터 #, ##
함수같은 매크로에서 replacement-list 내부에서 식별자 앞에 # 오퍼레이터를 붙이면 결과를 ""로 묶게 된다. 내부에 "가 있으면 \도 자동으로 추가된다.
#define showlist(...) puts(#__VA_ARGS__)
showlist() ; --> puts("") ;
showlist(1,"x", int); --> puts("1, \"x\", int") ;
repplacement-list 내부에 연속된 두 개의 식별자 사이에 ## 오펴레이터가 추가되면 파라미터들을 두 식별자로 바꾸게 된다. 이 작업은 스트링을 붙여서 인식하게 된다.
#define Func(x) Func ##x
Func(1) --> Func1
Func(2) --> Func2
#include <iostream>
#define FUNC(name, val) int FUNC_##name() { return val ; }
FUNC(aaa, 10)
FUNC(bbb, 20)
FUNC(ccc, 30)
int main() {
std::cout << FUNC_aaa() << std::endl ;
std::cout << FUNC_bbb() << std::endl ;
std::cout << FUNC_ccc() << std::endl ;
return 0 ;
}
10, 20, 30이 출력된다.
FUNC(aaa, 10) --> int FUNC_aaa() { return 10 ; }
위와 같이 풀린다.
+ Predefined macro
__FILE__ ; 현재 소스 파일명으로 확장 (string)
__LINE__ ; 현재 라인 번호 (int)
__DATE__ ; 컴파일 날짜(translation date) "Mmm dd yyyy"
__TIME__ ; 컴파일 시간 "hh:mm:ss"
- 소스 파일 포함 ; include
#include <filename>
#include "filename"
해당 파일을 그 위치에 포함시킨다.
C, C++ 라이브러리는 표준 include디렉터리에서 불러들인다. (include 디렉터리는 컴파일 옵션으로 설정할 수 있다.)
""로 지정하면 소스파일의 디렉터리에서 검색한다. 못찾으면 표준 include 디렉터리에서 찾는다.
nested된 include에 의해 반복될 경우를 막기위해 다음과 같이 한 번한 include되게 한다.
#ifndef _FOO_H_
#define _FOO_H_
// to do..
#endif
pragma를 지원하는 컴파일러에서 아래와 같이 사용하면 위 효과를 동일하게 할 수 있다.
#pragma once
- 에러 지시
#error error_message
위 지시자를 만나면 에러 메시지를 출력한다. 컴파일 시점에서 발생한다.
#if !WINDOWS_XP && !WINDOWS_7
#error Please check your OS
#endif
- 파일명과 줄번호 정보
#line lineno
#line lineno "filename"
다음 줄 라인 번호를 lineno로 변경한다. 매크로 __LINE__ 값이 바뀐다.
뒤에 파일명을 주면 __FILE__ 매크로인 파일명이 바뀐다.
#line 777 "test.cc"
assert(2+2==5) ;
위 소스를 컴파일 할 경우, test.cc:777 에서 assertion 에러가 발생한다.
'Develop > C&CPP' 카테고리의 다른 글
if or 비교시 어떤 식의 성공으로 진입했는지 구분 (0) | 2018.07.13 |
---|---|
Numerics library (0) | 2018.06.21 |
deque (0) | 2018.06.13 |
stack / queue (0) | 2018.06.12 |
cstring (0) | 2018.06.11 |