문제

나는 C에서 #undef의 실질적인 사용에 대해 궁금합니다. 저는 K & R을 통해 일하고 있으며 사전 프로세서에 달려 있습니다. 이 중 대부분은 물질적 인 I (다소)이 이해되었지만 90 페이지 (제 2 판)의 무언가가 나에게 붙어있었습니다.

이름은 정의되지 않을 수 있습니다 #undef, 일반적으로 일상이 매크로가 아니라 실제로 기능이되도록하기 위해

#undef getchar

int getchar(void) { ... }

이것은 누군가를 방어하는 일반적인 관행입니까? #define-기능과 같은 이름의 매크로를 사용하십니까? 아니면 실제로는 일어나지 않을 샘플이 더 많습니까? (예 : 그의 권리, 잘못 또는 미친 마음 속에 아무도 다시 작성해서는 안됩니다. getchar(), 그래서 그것은 일어나지 않아야합니다.) 자신의 기능 이름으로, 당신은 이것을해야한다고 생각합니까? 다른 사람들이 사용할 수있는 라이브러리를 개발하는 경우 그 변화가 있습니까?

도움이 되었습니까?

해결책

그것이하는 일

Plauger 's를 읽으면 표준 C 라이브러리 (1992), 당신은 그것을 볼 것입니다 <stdio.h> 헤더는 제공 할 수 있습니다 getchar() 그리고 getc() 기능과 같은 매크로로 (특별한 권한 포함 getc() 파일 포인터 인수를 두 번 이상 평가하려면!). 그러나 매크로를 제공하더라도 구현은 주로 동일한 작업을 수행하는 실제 기능을 제공하여 주로 호출되는 기능 포인터에 액세스 할 수 있어야합니다. getchar() 또는 getc() 그리고 그것을 다른 기능으로 전달하십시오.

즉, 수행함으로써 :

#include <stdio.h>
#undef getchar

extern int some_function(int (*)(void));

int core_function(void)
{
   int c = some_function(getchar);
   return(c);
}

서면으로 core_function() 꽤 의미가 없지만 요점을 보여줍니다. 당신은 똑같은 일을 할 수 있습니다 isxxxx() 매크로 <ctype.h> 예를 들어.

일반적으로, 당신은 그렇게하고 싶지 않습니다 - 당신은 일반적으로 매크로 정의를 제거하고 싶지 않습니다. 그러나 실제 기능이 필요할 때, 당신은 그것을 붙잡을 수 있습니다. 라이브러리를 제공하는 사람들은 표준 C 라이브러리의 기능을 좋은 효과로 모방 할 수 있습니다.

거의 필요하지 않습니다

또한 명시 적으로 사용할 필요가 거의없는 이유 중 하나입니다. #undef 작성하여 매크로 대신 함수를 호출 할 수 있기 때문입니다.

int c = (getchar)();

이후의 토큰 때문입니다 getchar 아닙니다 (, 그것은 함수와 같은 매크로의 호출이 아니므로 함수에 대한 참조 여야합니다. 마찬가지로 위의 첫 번째 예는 #undef.

매크로 오버라이드를 사용하여 자신의 기능을 구현하는 경우 설명되지 않으면 약간 혼란 스러울 수 있지만이를 효과적으로 사용할 수 있습니다.

/* function.h */
…
extern int function(int c);
extern int other_function(int c, FILE *fp);
#define function(c) other_function(c, stdout);
…
/* function.c */

…

/* Provide function despite macro override */
int (function)(int c)
{
    return function(c, stdout);
}

기능 정의 라인은 토큰 이후에 매크로를 호출하지 않습니다. function 아니다 (. 그만큼 return 라인은 매크로를 호출합니다.

다른 팁

매크로는 종종 대량의 코드를 생성하는 데 사용됩니다. 종종 현지화 된 사용법이며 안전합니다. #undef 이름 충돌을 피하기 위해 특정 헤더의 끝에있는 모든 도우미 매크로는 실제 생성 된 코드 만 다른 곳에서 가져오고 코드를 생성하는 데 사용되는 매크로는 그렇지 않습니다.

/편집 : 예를 들어, 나는 이것을 사용하여 스트러크를 생성했습니다. 다음은 실제 프로젝트에서 발췌 한 것입니다.

#define MYLIB_MAKE_PC_PROVIDER(name) \
    struct PcApi##name { \
        many members …
    };

MYLIB_MAKE_PC_PROVIDER(SA)
MYLIB_MAKE_PC_PROVIDER(SSA)
MYLIB_MAKE_PC_PROVIDER(AF)

#undef MYLIB_MAKE_PC_PROVIDER

전처리 기 때문에 #defineS는 모두 하나의 글로벌 네임 스페이스에 있으며, 특히 타사 라이브러리를 사용할 때 네임 스페이스 충돌이 쉽습니다. 예를 들어, 이름이 지정된 함수를 만들려면 OpenFile, 헤더 파일이 있기 때문에 올바르게 컴파일되지 않을 수 있습니다. <windows.h> 토큰을 정의합니다 OpenFile 어느 쪽이든지도 OpenFileA 또는 OpenFileW (IF에 따라 UNICODE 정의되었는지 여부). 올바른 해결책은 다음과 같습니다 #undef OpenFile 기능을 정의하기 전에.

나는 매크로 일 때만 사용합니다 #included 파일은 내 함수 중 하나를 방해하고 있습니다 (예 : 이름이 동일). 그럼 내가 #undef 매크로는 내 자신의 기능을 사용할 수 있습니다.

Jonathan Leffler가 당신에게 정답을 주었다고 생각합니다. 다음은 #undef를 사용하는 매우 드문 경우입니다. 일반적으로 매크로는 많은 기능 내에서 재사용 할 수 있어야합니다. 그렇기 때문에 파일 상단 또는 헤더 파일에서 정의하는 이유입니다. 그러나 때로는 매크로로 단축 될 수있는 함수 내부에 반복적 인 코드가 있습니다.


int foo(int x, int y)
{
#define OUT_OF_RANGE(v, vlower, vupper) \
    if (v < vlower) {v = vlower; goto EXIT;} \
    else if (v > vupper) {v = vupper; goto EXIT;}

    /* do some calcs */
    x += (x + y)/2;
    OUT_OF_RANGE(x, 0, 100);
    y += (x - y)/2;
    OUT_OF_RANGE(y, -10, 50);

    /* do some more calcs and range checks*/
    ...

EXIT:
    /* undefine OUT_OF_RANGE, because we don't need it anymore */
#undef OUT_OF_RANGE
    ...
    return x;
}

독자 에게이 매크로가 함수 내부에서만 유용하다는 것을 보여주기 위해서는 끝에 정의되지 않습니다. 나는 누구나 그런 해킹 매크로를 사용하도록 격려하고 싶지 않습니다. 그러나 만약 당신이해야한다면, #undef를 마지막에.

이것은 당신의 기능과 같은 이름의 매크로를 #정의하는 사람을 방어하는 일반적인 관행입니까? 아니면 실제로는 일어나지 않을 샘플이 더 많습니까? (예를 들어, 그의 권리, 잘못 또는 미친 마음 속에 아무도 getchar ()를 다시 쓰지 않아야하므로 등장해서는 안됩니다.)

둘 다 조금. 좋은 코드는 사용할 필요가 없습니다 #undef, 그러나 작업해야 할 나쁜 코드가 많이 있습니다. #undef 누군가가 같은 속임수를 뽑을 때 귀중한 것으로 판명 될 수 있습니다 #define bool int.

글로벌 네임 스페이스 오염 된 매크로 문제를 해결하는 것 외에도 또 다른 사용 #undef 매크로가 다른 장소에서 다른 행동을 가져야하는 상황입니다. 이것은 정말 일반적인 시나리오는 아니지만 떠오르는 부부는 다음과 같습니다.

  • 그만큼 assert 매크로는 코드의 일부에서 디버깅을 수행하지만 다른 코드는 아니지만 컴파일 장치 중간에서 정의를 변경할 수 있습니다. 에 추가 assert 그 자체가 필요합니다 #undef'이것을하기 위해 NDEBUG 원하는 동작을 재구성하기 위해 매크로를 재정의해야합니다. assert

  • 매크로를 사용하여 변수를 다음 extern, 그러나 매크로는 헤더/선언이 변수를 정의하는 데 사용되는 단일 케이스에 대해 아무것도 재정의되지 않습니다.

(나는 이것이 반드시 좋은 기술이라고 말하는 것은 아닙니다.

/* globals.h */
/* ------------------------------------------------------ */
#undef GLOBAL
#ifdef DEFINE_GLOBALS
#define GLOBAL
#else
#define GLOBAL extern
#endif

GLOBAL int g_x;
GLOBAL char* g_name;
/* ------------------------------------------------------ */



/* globals.c */
/* ------------------------------------------------------ */
#include "some_master_header_that_happens_to_include_globals.h"

/* define the globals here (and only here) using globals.h */
#define DEFINE_GLOBALS
#include "globals.h"

/* ------------------------------------------------------ */

매크로를 방어 할 수 있다면 undef 시설이 있어야합니다.

내가 사용하는 메모리 트래커는 자체 신규/삭제 매크로를 정의하여 파일/라인 정보를 추적합니다. 이 매크로는 SC ++ l을 깨뜨립니다.

#pragma push_macro( "new" )
#undef new
#include <vector>
#pragma pop_macro( "new" )

보다 구체적인 질문과 관련하여 : 네임 스페이스는 종종 식별자와 함께 라이브러리 기능을 접두사하여 C에서 ATED입니다.

맹목적으로 방어하는 매크로는 혼란을 더하고 유지 보수를 줄이며 원래 행동에 의존하는 것들을 깨뜨릴 수 있습니다. 당신이 강요된 경우, 적어도 푸시/팝을 사용하여 다른 곳에서 원래 행동을 보존하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top