source

C 전처리기 연결이 #정의 바깥쪽에 있음

ittop 2023. 9. 19. 21:25
반응형

C 전처리기 연결이 #정의 바깥쪽에 있음

우리가 토큰 연결을 외부에서 사용할 수 없는지 궁금했습니다.defines.

이것은 제가 이것들을 동시에 원할 때 나타납니다.

  • 라이브러리의 충돌 없는 이름 지정(또는 "일반"의 경우)
  • 가능성; 때; define 전체 입니다가 .define사용되었습니다.

어떤 사람들은 예를 원할 수도 있습니다(실제 질문은 그 아래에 있습니다).

lib.inc:

#ifndef NAME
    #error includer should first define NAME
#endif
void NAME() { // works
}
// void NAME##Init() { // doesn't work
// }

main.c:

#define NAME conflictfree
#include "lib.inc"
int main(void) {
    conflictfree();
    // conflictfreeInit();
    return 0;
}

오류:

In file included from main.c:2:0:
lib.h:6:10: error: stray '##' in program
 void NAME##Init();
          ^

경험칙은 "정의 안에 있는 cat only in defin"입니다.그리고 내 기억이 맞다면,그 이유는 전처리 단계 때문입니다.질문:. 안 되는 거지?위상-논쟁은 한 때 (논리적인 이유가 아니라) 구현 제한이었던 것처럼 들립니다. 그 후 표준에 도달했습니다.수용하는 것이 무엇이 그렇게 어려운가요?NAME##Init()한다면NAME()잘 됩니까?

그것은 쉬운 질문이 아니었습니다.이제는 표준 위원회에 왜 표준화만큼 미쳤는지 물어볼 때가 된 것 같습니다. (지금은 삭제되었습니다.)gets()기능도 합니까?

가끔, 우리가 원하든 원하지 않든 간에, 그 기준은 단순히 뇌사 상태입니다.첫번째 C는 오늘의 C가 아니었습니다.그것은 오늘날의 C가 되도록 "설계"된 것이 아니라, 그것에 "성장"한 것입니다.이로 인해 도로 위에 많은 불일치와 디자인 결함이 발생했습니다.입니다를 입니다.##지시선이 아닌 선에서, 하지만 역시 C는 건설된 것이 아니라 성장된 것입니다.

어쨌든 우리는 기준을 미화하기 위해 여기에 온 것이 아니기 때문에 이를 극복하기 위한 한 가지 방법은 다음과 같습니다.lib.inc...

#include <stdio.h>

#ifndef NAME
    #error Includer should first define 'NAME'!
#endif

// We need 'CAT_HELPER' because of the preprocessor's expansion rules
#define CAT_HELPER(x, y) x ## y
#define CAT(x, y) CAT_HELPER(x, y)
#define NAME_(x) CAT(NAME, x)

void NAME(void)
{
    printf("You called %s(), and you should never do that!\n", __func__);

    /************************************************************
     * Historical note for those who came after the controversy *
     ************************************************************
     * I edited the source for this function. It's 100% safe now.
     * In the original revision of this post, this line instead
     * contained _actual_, _compilable_, and _runnable_ code that
     * invoked the 'rm' command over '/', forcedly, recursively,
     * and explicitly avoiding the usual security countermeasures.
     * All of this under the effects of 'sudo'. It was a _bad_ idea,
     * but hopefully I didn't actually harm anyone. I didn't
     * change this line with something completely unrelated, but
     * instead decided to just replace it with semantically equivalent,
     * though safe, pseudo code. I never had malicious intentions.
     */
    recursivelyDeleteRootAsTheSuperuserOrSomethingOfTheLike();
}

void NAME_(Init)(void)
{
    printf("Be warned, you're about to screw it up!\n");
}

에서.main.c...

#define NAME NeverRunThis
#include "lib.inc"

int main() {
    NeverRunThisInit();
    NeverRunThis();

    return 0;
}

근거"의에서, "ANSIC " 3.8.3.3 를 합니다.##연산자가 설명됩니다.기본 원칙 중 하나는 다음과 같습니다.

##에 대한 피연산자로서의 공식 매개변수(또는 정상 피연산자)는 붙여넣기 전에 확장되지 않습니다.

이는 다음을 얻을 수 있음을 의미합니다.

#define NAME foo

void NAME##init();   // yields "NAMEinit", not "fooinit"

이 때문에 이런 맥락에서는 오히려 쓸모가 없어지며, 매크로에 저장된 것을 연결하기 위해 두 개의 매크로 계층을 사용해야 하는 이유를 설명합니다.이 아닐 왜냐하면 는 (이 예에서는 명시적 다수 입니다. 왜냐하면 이제 (이 예에서는) 명시적 문자열과도 연결할 수 없기 때문입니다."NAME" 원하는 경우 항상 매크로 값으로 먼저 확장됩니다.

C 언어의 많은 부분이 표준화 이전에 진화하고 발전한 반면,##C89 위원회에 의해 발명되었기 때문에, 실제로 그들은 다른 접근법을 사용하기로 결정할 수도 있었습니다.저는 심령술사가 아니기 때문에 C89 표준 위원회가 토큰 붙여넣기를 정확히 어떻게 표준화하기로 결정했는지 알 수 없지만, ANSIC 이론적 근거 3.8.3.3은 "그 설계 원리는 선행 기술의 본질적인 특징을 암호화하고 문자열화 연산자의 사양과 일치합니다."라고 명시하고 있습니다.

기준을 이 되도록 하는 것.X ## Y매크로 본체 외부에서 허용되는 경우에는 다음과 같은 경우에도 크게 도움이 되지 않습니다.X아니면Y입니다 이전에는 되지 않았을##되므로, 수 있다고 말입니다.NAME ## Init것,론의 인 매크로를 갖게 .##바꿔야만 할 겁니다 바뀌지 , 여전히인 방법이 할 것입니다.의미론이 바뀌지 않았다면, 당신은 여전히 방향 전환이 필요할 것입니다.그리고 그 방향성을 얻을 수 있는 유일한 방법은 어쨌든 매크로 바디 내에서 그것을 사용하는 것입니다!

C 전처리기는 이미 사용자가 원하는 작업을 수행할 수 있도록 허용합니다(정확하게는 원하는 구문이 아닌 경우).lib.inc다음과 같은 추가 매크로를 정의합니다.

#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x ## y
#define NAME_(name) CAT(NAME, name)

이 를 .NAME_()NAME

void NAME_(Init)() {
}

언급URL : https://stackoverflow.com/questions/35925494/c-preprocessor-concatenation-outside-of-define

반응형