문제
나는 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
전처리 기 때문에 #define
S는 모두 하나의 글로벌 네임 스페이스에 있으며, 특히 타사 라이브러리를 사용할 때 네임 스페이스 충돌이 쉽습니다. 예를 들어, 이름이 지정된 함수를 만들려면 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입니다.
맹목적으로 방어하는 매크로는 혼란을 더하고 유지 보수를 줄이며 원래 행동에 의존하는 것들을 깨뜨릴 수 있습니다. 당신이 강요된 경우, 적어도 푸시/팝을 사용하여 다른 곳에서 원래 행동을 보존하십시오.