itsource

어떻게 하면 C 프리프로세서와 두 번 연결하여 "arg ##_# MACRO"처럼 매크로를 확장할 수 있습니까?

mycopycode 2022. 7. 21. 23:39
반응형

어떻게 하면 C 프리프로세서와 두 번 연결하여 "arg ##_# MACRO"처럼 매크로를 확장할 수 있습니까?

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

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

int NAME(some_function)(int a);

도 이 NAME()을 turns그로

int some_function_VARIABLE(int a);

보다는

int some_function_3(int a);

VARILE의 VARILE의 VARILE의 VARILE의 VARILE의 간단하게 할 수 있습니다.#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 Rou는 왜 이것이 두 가지 수준의 간접이 필요한지 물었다.경솔한 답변은 표준이 그렇게 작동하도록 요구하기 때문입니다. 문자열화 연산자에서도 이와 동등한 기술이 필요하게 되는 경향이 있습니다.

C99 표준의 섹션 6.10.3은 '매크로 대체'를 다루고 6.10.3.1은 '인수 대체'를 다룬다.

함수형 매크로의 기동 인수가 특정되면 인수 치환이 이루어집니다. 리스트의 에 「」( 「a」)가 않는 한, 치환 리스트의 파라미터.# ★★★★★★★★★★★★★★★★★」## 그 에 '''가 붙습니다.##전처리 토큰(아래 참조)은 포함된 모든 매크로가 확장되면 대응하는 인수로 대체됩니다.대체되기 전에 각 인수의 전처리 토큰은 전처리 파일의 나머지 부분을 구성하는 것처럼 완전히 매크로로 대체됩니다. 하다

시 " " "NAME(mine)'내 것인수는 'mine'으로 완전히 확장되어 치환 문자열로 대체됩니다.

EVALUATOR(mine, VARIABLE)

이제 매크로 EVALUATOR가 검출되고 인수는 'mine'과 'VARILE'로 분리되며, 이후 인수는 '3'으로 완전히 확장되어 대체 문자열로 대체됩니다.

PASTER(mine, 3)

이 조작은 다른 규칙(6.10.3.3 '## 연산자'):

앞 경우##

매크로 매크로 에, 「」의 각 는 「」로 바뀝니다.##(인수가 아닌) 치환 리스트의 전처리 토큰이 삭제되고 앞의 전처리 토큰이 다음 전처리 토큰과 연결됩니다.

대체 에는 '비교체하다'가 들어있습니다.x에 어 followed가 붙는다.##, 「」도 있습니다.##에 어 followed가 붙는다.y하다

mine ## _ ## 3

하고,##토큰을 연결하면 "mine"과 "_" 및 "3"이 결합되어 다음과 같이 됩니다.

mine_3

이것은 바람직한 결과입니다.


원래 질문을 보면 코드는 다음과 같습니다('some_function'이 아닌 'mine'을 사용하도록 조정됨).

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

NAME(mine)

NAME에 대한 인수는 분명히 '내 것'이며, 그것은 완전히 확장되어 있습니다.
6 6.10.3.3을

mine ## _ ## VARIABLE

때, 그,##연산자가 삭제되어 다음 위치에 매핑됩니다.

mine_VARIABLE

질문에 보고된 그대로입니다.


기존 C 프리프로세서

Robert Rüger다음과 같이 묻습니다.

연산자 「」가 의 C 프리 ?##

그럴 수도 있고 아닐 수도 있습니다.프리프로세서에 따라 다릅니다.표준 프리프로세서의 장점 중 하나는 신뢰성 있게 동작하는 이 기능을 갖추고 있다는 것입니다만, 선행 표준 프리프로세서에 대해서는 다른 실장이 있었습니다.1가지 요건은 프리프로세서가 코멘트를 치환할 때 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(「」 「」 「」 「아예)

6(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 프리프로세서를 사용하는 것이 더 간단할 수 있습니다(일반적으로 컴파일러를 적절하게 설정하는 방법이 있습니다.

용도:

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

솔직히, 왜 이게 효과가 있는지 알고 싶지 않을 거예요.이게 왜 효과가 있는지 알면 직장에서도 이런 걸 아는 사람이 될 거고 다들 질문을 하러 올 거예요=)

의 알기 쉬운 영어 EVALUATOR

C규격의 모든 단어를 완전히 이해하지는 못했지만 Jonathan Leffler의 답변에 나타난 솔루션이 어떻게 기능하는지에 대한 합리적인 모델이라고 생각합니다.라고 조금 더 자세히 설명했습니다.제 이해가 틀렸다면, 제 이론을 깨트릴 수 있는 최소한의 예시로 알려주세요.

이 목적을 위해 매크로 확장은 다음 세 단계로 이루어진다고 생각할 수 있습니다.

  1. (prescan) 매크로 인수가 대체되었습니다.
    • 연결 또는 문자열화의 일부인 경우 확장되지 않고 매크로 콜에 지정된 문자열과 동일하게 대체됩니다.
    • 그렇지 않으면 먼저 완전히 확장되고 그 후에 교체됩니다.
  2. 문자열화 및 연결 발생
  3. 정의된 모든 매크로가 확장됩니다.

간접적이지 않은 단계별 예시

메인

#define CAT(x) pref_ ## x
#define Y a

CAT(Y)

다음으로 확장합니다.

gcc -E main.c

다음과 같은 것을 얻을 수 있습니다.

pref_Y

이유:

1: 서 1:Y입니다.CAT.

xpref_ ## x . 러므, 。Y확장 없이 그대로 붙여넣기:

pref_ ## Y

2단계: 연결이 이루어지면 다음과 같은 결과가 남습니다.

pref_Y

세 번째: 세 번째, 세 번째, 세 번째, 세 번째. ★★★★★★★★★★★★★★★★★.pref_Y이미 알려진 매크로가 아니기 때문에 그대로 남아 있습니다.

이 이론을 할 수 것은 이 입니다.pref_Y:

#define CAT(x) pref_ ## x
#define Y a
#define pref_Y asdf

CAT(Y)

결과는 다음과 같습니다.

asdf

pref_Y이치노

간접 사용의 단계별 예

단, 2단계 패턴을 사용하는 경우:

#define CAT2(x) pref_ ## x
#define CAT(x) CAT2(x)
#define Y a

CAT(Y)

다음과 같은 것을 얻을 수 있습니다.

pref_a

1: 서 1:CAT가됩니니다다

CAT(x)되어 있습니다.CAT2(x) 인수 , so 수 ,xCAT는 문자열화 뒤에만 합니다.문자열화는 다음 시간 이후에만 실행됩니다.CAT2이 순서에서는 확인할 수 없습니다.

therefore그 、Y, 2, 에서는 1, 2, 3단계로 합니다.여기에서는 1단계, 2단계, 3단계까지 확장되기 때문에 생략합니다.a ★★★★★★★★★★★★★★★★★★★★★★★★.aCAT2(x)★★★★

CAT2(a)

2단계: 실행할 문자열 지정이 없습니다.

세 번째: 세 번째입니다.요.CAT2(a)이치노

3.1: 3.1: 인수xCAT2pref_ ## x입력 a대로 、 공: :

pref_ ## a

스텝 3.2: 문자열화:

pref_a

3번, 3번, 3번역시 3번입니다. pref_a매크로가 아니기 때문에, 이것으로 끝입니다.

GCC 인수 사전 검색 문서

이 문제에 관한 GCC의 문서도 읽을 가치가 있습니다.https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html

보너스: 이러한 규칙에 의해 네스트된 콜이 무한 확장되지 않도록 하는 방법

다음 사항을 고려하십시오.

#define f(x) (x + 1)

f(f(a))

다음과 같이 확장됩니다.

((a + 1) + 1)

무궁무진하게 되는 대신.

자세히 설명하겠습니다.

1: 계 : 부f 됩니다.x = f(a).

「」의 f, " "x는, .(x + 1)f따라서 교체하기 전에 먼저 완전히 확장됩니다.

1.1: 1.1: 인수 1.1을 완전히 합니다.x = f(1), 2 3에 1, 2, 3을 합니다.x = (a + 1).

이제 이 확장형 으로 넘어가겠습니다.x「」와 같은 (a + 1).을 사용하다f★★★★

((a + 1) + 1)

스텝 2와 3: 문자열화도 없고 확장할 매크로도 없기 때문에 별로 발생하지 않습니다.

언급URL : https://stackoverflow.com/questions/1489932/how-can-i-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-ar

반응형