programing tip

"arg ## _ ## MACRO"와 같이 C 전처리 기와 두 번 연결하고 매크로를 확장하는 방법은 무엇입니까?

itbloger 2020. 6. 22. 08:05
반응형

"arg ## _ ## MACRO"와 같이 C 전처리 기와 두 번 연결하고 매크로를 확장하는 방법은 무엇입니까?


일부 함수의 이름이 다음과 같은 매크로를 사용하여 특정 매크로 변수의 값에 의존하는 프로그램을 작성하려고합니다.

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

int NAME(some_function)(int a);

불행히도, 매크로 NAME()는 그것을

int some_function_VARIABLE(int a);

오히려

int some_function_3(int a);

그래서 이것은 분명히 잘못된 길입니다. 다행스럽게도 VARIABLE에 대한 가능한 값의 수가 적기 때문에 #if VARIABLE == n모든 경우를 개별적으로 나열하고 나열 할 수는 있지만 영리한 방법이 있는지 궁금합니다.


표준 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)

The argument to NAME is clearly 'mine' and that is fully expanded.
Following the rules of 6.10.3.3, we find:

mine ## _ ## VARIABLE

which, when the ## operators are eliminated, maps to:

mine_VARIABLE

exactly as reported in the question.


Traditional C Preprocessor

Robert Rüger asks:

Is there any way do to this with the traditional C preprocessor which does not have the token pasting operator ##?

Maybe, and maybe not — it depends on the preprocessor. One of the advantages of the standard preprocessor is that it has this facility which works reliably, whereas there were different implementations for pre-standard preprocessors. One requirement is that when the preprocessor replaces a comment, it does not generate a space as the ANSI preprocessor is required to do. The GCC (6.3.0) C Preprocessor meets this requirement; the Clang preprocessor from XCode 8.2.1 does not.

When it works, this does the job (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);

Note that there isn't a space between fun, and VARIABLE — that is important because if present, it is copied to the output, and you end up with mine_ 3 as the name, which is not syntactically valid, of course. (Now, please can I have my hair back?)

With GCC 6.3.0 (running cpp -traditional x-paste.c), I get:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_3(char *x);

With Clang from XCode 8.2.1, I get:

# 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);

Those spaces spoil everything. I note that both preprocessors are correct; different pre-standard preprocessors exhibited both behaviours, which made token pasting an extremely annoying and unreliable process when trying to port code. The standard with the ## notation radically simplifies that.

There might be other ways to do this. However, this does not work:

#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 generates:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_VARIABLE(char *x);

Close, but no dice. YMMV, of course, depending on the pre-standard preprocessor that you're using. Frankly, if you're stuck with a preprocessor that is not cooperating, it would probably be simpler to arrange to use a standard C preprocessor in place of the pre-standard one (there is usually a way to configure the compiler appropriately) than to spend much time trying to work out a way to do the job.


#define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)

int NAME(some_function)(int a);

Honestly, you don't want to know why this works. If you know why it works, you'll become that guy at work who knows this sort of thing, and everyone will come ask you questions. =)

Edit: if you actually want to know why it works, I'll happily post an explanation, assuming no one beats me to it.

참고URL : https://stackoverflow.com/questions/1489932/how-to-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-arg

반응형