표준 C 전 처리기
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)
extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
extern void mine_3(char *x);
$
두 가지 수준의 간접
다른 답변에 대한 의견에서 Cade Roux 는 왜 두 가지 수준의 간접 지식 이 필요한지 물었습니다 . 플립 팬트의 대답은 그것이 표준이 작동하는 방식이기 때문입니다. 문자열 화 연산자와 동등한 트릭이 필요하다는 것을 알았습니다.
C99 표준의 섹션 6.10.3은 '매크로 대체'를 다루고 6.10.3.1은 '인수 대체'를 다루고 있습니다.
함수형 매크로의 호출에 대한 인수가 식별되면 인수 대체가 수행됩니다. 교체리스트에 변수가없는 한 선행 #
또는 ##
전처리 또는 토큰 따르는 ##
전처리 모든 매크로 내부에 전개 된 후 포함 된 (아래 참조) 토큰 대응하는 인자로 대체된다. 대체되기 전에 각 인수의 전처리 토큰은 마치 전처리 파일의 나머지 부분을 구성하는 것처럼 완전히 매크로로 대체됩니다. 사용 가능한 다른 전처리 토큰이 없습니다.
호출 NAME(mine)
에서 인수는 'mine'입니다. 그것은 '광산'으로 완전히 확장되었습니다. 그런 다음 대체 문자열로 대체됩니다.
EVALUATOR(mine, VARIABLE)
이제 매크로 EVALUATOR가 발견되고 인수는 'mine'및 'VARIABLE'로 분리됩니다. 후자는 '3'으로 완전히 확장되고 대체 문자열로 대체됩니다.
PASTER(mine, 3)
이 작업은 다른 규칙 (6.10.3.3 '## 연산자')에 의해 처리됩니다.
함수와 유사한 매크로의 대체 목록에서 매개 변수 바로 앞에 또는 ##
전처리 토큰이 오는 경우 해당 매개 변수는 해당 인수의 전처리 토큰 순서로 대체됩니다. [...]
객체 형 및 함수형 매크로 호출 모두에 대해 교체 용 매크로를 대체 할 추가 매크로 이름으로 교체 목록을 재검토하기 전에 교체 형 목록의 ##
전처리 토큰 (인수가 아닌) 의 각 인스턴스 가 삭제되고 이전 전처리 토큰이 연결됩니다. 다음 전처리 토큰으로.
그래서, 대체 목록이 포함 x
뒤에 ##
또한 ##
다음 y
; 그래서 우리는 :
mine ## _ ## 3
##
토큰을 제거하고 양쪽에 토큰을 연결하면 'mine'과 '_'및 '3'을 결합하여 다음을 생성합니다.
mine_3
원하는 결과입니다.
우리가 원래의 질문을 보면, 코드는 ( 'some_function'대신 'mine'을 사용하도록 적응되었습니다) :
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
NAME에 대한 논쟁은 분명히 '광산'이며 완전히 확장되었습니다.
6.10.3.3의 규칙에 따라 다음을 찾습니다.
mine ## _ ## VARIABLE
이는의 경우 ##
사업자가 제거에 매핑 :
mine_VARIABLE
질문에보고 된대로.
전통적인 C 전 처리기
Robert Rüger 가 묻습니다 .
토큰 붙여 넣기 연산자가없는 기존 C 프리 프로세서를 사용하여이 작업을 수행 할 수있는 방법이 ##
있습니까?
어쩌면 아닐 수도 있습니다. 전처리기에 따라 다릅니다. 표준 전 처리기의 장점 중 하나는이 기능이 안정적으로 작동하는 반면, 사전 표준 전처리기에는 다른 구현이 있다는 것입니다. 하나의 요구 사항은 전처리 기가 주석을 대체 할 때 ANSI 전처리 기가 요구하는대로 공간을 생성하지 않아야한다는 것입니다. GCC (6.3.0) C 전처리 기는이 요구 사항을 충족합니다. XCode 8.2.1의 Clang 전처리 기는 그렇지 않습니다.
작동하면 다음 작업을 수행합니다 ( x-paste.c
).
#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
사이에 공간이 아님을 참고 fun,
하고 VARIABLE
있는 경우,이 출력에 복사하고, 당신이 결국 때문에 중요하다 - mine_ 3
물론, 구문 적으로 유효하지 않은 이름으로가. (이제 머리 좀 돌려 주 시겠어요?)
GCC 6.3.0 (실행 중 cpp -traditional x-paste.c
)을 사용하면 다음을 얻습니다.
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_3(char *x);
XCode 8.2.1의 Clang을 사용하면 다음을 얻을 수 있습니다.
# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2
extern void mine _ 3(char *x);
그 공간은 모든 것을 망칩니다. 두 전처리 기가 모두 정확하다는 것을 알고 있습니다. 서로 다른 사전 표준 프리 프로세서는 두 가지 동작을 모두 나타내므로 코드를 포트하려고 할 때 토큰 붙여 넣기가 매우 성 가시고 신뢰할 수없는 프로세스가되었습니다. ##
표기법 이있는 표준 은 그것을 근본적으로 단순화합니다.
다른 방법이있을 수 있습니다. 그러나 이것은 작동하지 않습니다.
#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
GCC는 다음을 생성합니다.
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_VARIABLE(char *x);
가까이 있지만 주사위는 없습니다. 물론 YMMV는 사용중인 사전 표준 전처리기에 따라 다릅니다. 솔직히, 당신이 협력하지 않는 전처리기를 고집하고 있다면, 표준보다 전 처리기 대신에 표준 C 전처리기를 사용하는 것이 더 간단 할 것입니다. 일을하는 방법을 찾기 위해 많은 시간을 보내십시오.