programing

## 프리프로세서 오퍼레이터와 gotcha의 어플리케이션은 무엇입니까?

prostudy 2022. 7. 26. 22:15
반응형

## 프리프로세서 오퍼레이터와 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

반응형