## 프리프로세서 오퍼레이터와 gotcha의 어플리케이션은 무엇입니까?
이전 질문에서도 언급했듯이, 저는 K&R을 통해 일하고 있으며, 현재 프리프로세서에 빠져 있습니다. 흥미로운 것 중 는, 지금까지 C를 했던 것 중 것 중인데, 그것은 C는 C를 배우려고 했던 것입니다.##K&R에 :
연산자 " " "
##에 매크로 확장 중에 실제 인수를 연결하는 방법을 나타냅니다.가 에 해 있는 .##""로##주위의 공백이 제거되고 결과가 다시 표시됩니다.를 들어, 「」는, 「」라고 하는 것입니다.paste두 논거를
#define paste(front, back) front ## back
paste(name, 1)는 토큰을 .name1.
현실세계에서 이걸 어떻게, 왜 사용하는 거죠?그 사용의 실제적인 예는 무엇이며, 고려해야 할 사항이 있습니까?
페이스트를 할 때 해야 할 (''Knowledge paste## #.' 라고 말합니다.
이렇게 하지 않고 토큰 붙여넣기 오퍼레이터에게 전달된 항목이 매크로 자체일 경우 원하는 결과가 아닐 수 있습니다.
#include <stdio.h>
#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)
#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x
#define SOME_MACRO function_name
int main()
{
printf( "buggy results:\n");
printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
printf( "\n" "desired result:\n");
printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}
출력:
buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)
desired result:
function_name21
CrashRpt: ##을 사용하여 매크로 멀티바이트 문자열을 Unicode로 변환
CrashRpt(Crash Reporting Library)의 중요한 용도는 다음과 같습니다.
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
//Note you need a WIDEN2 so that __DATE__ will evaluate first.
여기서는 1바이트/char 문자열 대신 2바이트 문자열을 사용합니다.이것은 아마 정말 무의미해 보이지만, 그들은 그럴만한 이유가 있다.
std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);
날짜 및 시간이 포함된 문자열을 반환하는 다른 매크로와 함께 사용합니다.
「 」를 .L __ DATE __컴파일 오류가 발생합니다.
Windows:범용 유니코드 또는 멀티바이트 문자열에 ## 사용
Windows 에서는, 다음과 같은 것을 사용합니다.
#ifdef _UNICODE
#define _T(x) L ## x
#else
#define _T(x) x
#endif
★★★★★★★★★★★★★★★★★._T됩니다.
클린 액세스 및 수식자 이름에 사용되는 다양한 라이브러리:
또, 코드내에서 액세스와 수식어를 정의하는 것도 보았습니다.
#define MYLIB_ACCESSOR(name) (Get##name)
#define MYLIB_MODIFIER(name) (Set##name)
마찬가지로 다른 유형의 현명한 이름 작성에도 동일한 방법을 사용할 수 있습니다.
여러 개의 변수 선언을 동시에 작성하기 위해 사용하는 다양한 라이브러리:
#define CREATE_3_VARS(name) name##1, name##2, name##3
int CREATE_3_VARS(myInts);
myInts1 = 13;
myInts2 = 19;
myInts3 = 77;
매크로 매개 변수를 다른 매개 변수와 연결해야 할 때 토큰 붙여넣기를 사용할 수 있습니다.
템플릿에 사용할 수 있습니다.
#define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};
이 경우 LINKED_LIST(int)는 다음을 제공합니다.
struct list_int {
int value;
struct list_int *next;
};
마찬가지로 리스트 트래버설의 함수 템플릿을 작성할 수 있습니다.
컴파일러의 새로운 버전으로 업그레이드 할 때 발견한 것은 다음과 같습니다.
연산자의 ( 「 」 )## 때문에 또는 할 수 는 포터블이 아니기 때문에 불필요한 공백, 경고 또는 오류가 발생할 수 있습니다.
토큰 붙여넣기 연산자의 결과가 유효한 프리프로세서 토큰이 아닌 경우 토큰 붙여넣기 연산자는 불필요하며 유해할 수 있습니다.
예를 들어 토큰 붙여넣기 연산자를 사용하여 컴파일 시 문자열 리터럴을 구축하려고 할 수 있습니다.
#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a##+##b)
#define NS(a, b) STRINGIFY(a##::##b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));
컴파일러에 따라서는, 다음과 같은 결과가 출력됩니다.
1+2 std::vector
다른 컴파일러에서는 불필요한 공백이 포함됩니다.
1 + 2 std :: vector
상당히 최신 버전의 GCC(>=3.3 등)에서는 다음 코드를 컴파일할 수 없습니다.
foo.cpp:16:1: pasting "1" and "+" does not give a valid preprocessing token
foo.cpp:16:1: pasting "+" and "2" does not give a valid preprocessing token
foo.cpp:16:1: pasting "std" and "::" does not give a valid preprocessing token
foo.cpp:16:1: pasting "::" and "vector" does not give a valid preprocessing token
해결책은 C/C++ 연산자에 프리프로세서 토큰을 연결할 때 토큰 붙여넣기 연산자를 생략하는 것입니다.
#define STRINGIFY(x) #x
#define PLUS(a, b) STRINGIFY(a+b)
#define NS(a, b) STRINGIFY(a::b)
printf("%s %s\n", PLUS(1,2), NS(std,vector));
토큰 붙여넣기 연산자에 대한 자세한 내용은 GCC CPP 매뉴얼 장을 참조하십시오.
이것은 불필요한 반복을 하지 않기 위해 모든 상황에서 유용합니다.이맥스라이브러리에서 여러 기능을 로드하고 싶습니다.는 "foo" 에.fn_foo 。매크로를 합니다.하다
#define LOAD_IMGLIB_FN(lib,func) { \
fn_##func = (void *) GetProcAddress (lib, #func); \
if (!fn_##func) return 0; \
}
다음으로 사용할 수 있습니다.
LOAD_IMGLIB_FN (library, XpmFreeAttributes);
LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
LOAD_IMGLIB_FN (library, XpmReadFileToImage);
LOAD_IMGLIB_FN (library, XImageFree);
글자를 쓸 이 있습니다.fn_XpmFreeAttributes ★★★★★★★★★★★★★★★★★」"XpmFreeAttributes"(그 중 하나의 철자를 틀릴 위험이 있습니다.
Stack Overflow에 대한 이전 질문에서는 오류 발생 가능성이 높은 재입력 없이 열거형 상수에 대한 문자열 표현을 생성하는 부드러운 방법이 요구되었습니다.
이 질문에 대한 제 답변은 작은 프리프로세서 마법을 적용하면 이렇게 열거를 정의할 수 있다는 것을 보여주었습니다(예를 들어).;
ENUM_BEGIN( Color )
ENUM(RED),
ENUM(GREEN),
ENUM(BLUE)
ENUM_END( Color )
... 매크로 확장은 열거형(.h 파일)을 정의할 뿐만 아니라 일치하는 문자열 배열(.c 파일)도 정의합니다.
const char *ColorStringTable[] =
{
"RED",
"GREEN",
"BLUE"
};
문자열 테이블의 이름은 ## 연산자를 사용하여 매크로 파라미터(Color 등)를 StringTable에 붙여넣은 것에서 유래합니다.이러한 애플리케이션(트릭?)은 # 및 ## 연산자가 매우 중요한 부분입니다.
저는 C 프로그램에서 호출 규칙을 준수해야 하는 일련의 메서드에 대해 프로토타입을 올바르게 적용할 수 있도록 돕기 위해 이 프로그램을 사용합니다.어떤 면에서는, 이것은 직선 C에서 가난한 사람의 물체 방향에 사용할 수 있다.
SCREEN_HANDLER( activeCall )
다음과 같이 확장됩니다.
STATUS activeCall_constructor( HANDLE *pInst )
STATUS activeCall_eventHandler( HANDLE *pInst, TOKEN *pEvent );
STATUS activeCall_destructor( HANDLE *pInst );
이렇게 하면 다음과 같은 경우 모든 "파생" 개체에 대해 올바른 매개 변수화가 수행됩니다.
SCREEN_HANDLER( activeCall )
SCREEN_HANDLER( ringingCall )
SCREEN_HANDLER( heldCall )
위의 내용을 헤더 파일 등에 포함시킵니다.또한 정의를 변경하거나 "개체"에 메서드를 추가하려는 경우에도 유지관리 시 유용합니다.
SGlib은 ##을 사용하여 기본적으로 C의 템플릿을 퍼지합니다.함수 오버로드가 없으므로 ##을 사용하여 생성된 함수 이름에 유형 이름을 붙여 넣습니다.list_t라는 이름의 목록 유형이 있으면 sglib_list_t_concat 등의 함수를 얻을 수 있습니다.
내장용 비표준 C 컴파일러의 홈 롤 아사트에 사용합니다.
#define ASSERT(exp) if(!(exp)){ \
print_to_rs232("Assert failed: " ## #exp );\
while(1){} //Let the watchdog kill us
매크로에 의해 정의된 변수에 커스텀프리픽스를 추가할 때 사용합니다.예를 들어 다음과 같습니다.
UNITTEST(test_name)
확장 대상:
void __testframework_test_name ()
주로 이름 지정 규칙을 사용하고 매크로가 해당 이름 지정 규칙을 활용할 때 사용합니다.image_create(), image_activate() 및 image_release()의 메서드 패밀리가 여러 개 있을 수 있습니다.또한 file_create(), file_activate(), file_release() 및 mobile_create(), mobile_activate() 및 mobile_release().
오브젝트 라이프 사이클을 처리하기 위한 매크로를 작성할 수 있습니다.
#define LIFECYCLE(name, func) (struct name x = name##_create(); name##_activate(x); func(x); name##_release())
물론 일종의 "개체의 최소 버전"만이 이러한 명명 규칙에 적용되는 것은 아닙니다. 대부분의 명명 규칙은 공통 하위 문자열을 사용하여 이름을 형성합니다.기능명(상기와 같음), 필드명, 변수명, 기타 대부분의 기능을 사용할 수 있습니다.
WinCE에서의 중요한 용도:
#define BITFMASK(bit_position) (((1U << (bit_position ## _WIDTH)) - 1) << (bit_position ## _LEFTSHIFT))
레지스터 비트에 대한 설명을 정의하는 동안 다음 작업을 수행합니다.
#define ADDR_LEFTSHIFT 0
#define ADDR_WIDTH 7
또한 BITFMASK를 사용할 때는 다음을 사용합니다.
BITFMASK(ADDR)
로깅에 매우 유용합니다.다음 작업을 수행할 수 있습니다.
#define LOG(msg) log_msg(__function__, ## msg)
또는 컴파일러가 함수 및 기능을 지원하지 않는 경우:
#define LOG(msg) log_msg(__file__, __line__, ## msg)
위의 "함수"는 메시지를 기록하고 어떤 함수가 메시지를 기록했는지 정확하게 보여줍니다.
C++ 구문이 정확하지 않을 수 있습니다.
언급URL : https://stackoverflow.com/questions/216875/what-are-the-applications-of-the-preprocessor-operator-and-gotchas-to-conside
'programing' 카테고리의 다른 글
| Java에서 무한대를 구현하는 방법2 (0) | 2022.07.26 |
|---|---|
| Vuetify 데이터 테이블에서 Vuex 상태를 올바르게 업데이트하려면 어떻게 해야 합니까? (0) | 2022.07.26 |
| C++에서 로컬 환경 변수 설정 (0) | 2022.07.26 |
| 왜 사람들은 여전히 자바에서 원시형을 사용하는가? (0) | 2022.07.26 |
| C의 구조 부재 기본값 (0) | 2022.07.26 |