문제

이전의 많은 질문에서 언급했듯이 K & R을 통해 일하고 있으며 현재 사전 처리기에 있습니다. 더 흥미로운 것 중 하나는 C를 배우려는 이전의 이전 시도에서 내가 전에 알지 못했던 것입니다. ## 전처리 작업자. K & R에 따르면 :

전처리 연산자 ##매크로 확장 중에 실제 인수를 연락하는 방법을 제공합니다. 교체 텍스트의 매개 변수가 ##, 매개 변수는 실제 인수, ## 주변 공백이 제거되고 결과가 다시 스크로트됩니다. 예를 들어, 매크로 paste두 가지 주장을 연결합니다.

#define paste(front, back) front ## back

그래서 paste(name, 1) 토큰을 만듭니다 name1.

누군가가 현실 세계에서 어떻게 그리고 왜 이것을 사용할 것인가? 사용의 실제적인 예는 무엇이며 고려해야 할 점이 있습니까?

도움이 되었습니까?

해결책

Crashrpt : ## 사용을 사용하여 매크로 멀티 바이트 문자열을 유니 코드로 변환

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 바이트당 문자열 대신 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;

다른 팁

토큰 페이스트를 사용할 때 알아야 할 한 가지.##') 또는 문자열 (')#') 전처리 연산자는 모든 경우에 제대로 작동하기 위해 추가 간접 수준을 사용해야한다는 것입니다.

이 작업을 수행하지 않고 토큰을 포장하는 연산자에게 전달 된 항목이 매크로 자체라면 원하는 결과가 아닌 결과를 얻을 수 있습니다.

#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

다음은 새 버전의 컴파일러로 업그레이드 할 때 실행 한 gotcha입니다.

토큰 이용 연산자의 불필요한 사용 (##)는 포트가 불가능하며 바람직하지 않은 공백, 경고 또는 오류를 생성 할 수 있습니다.

토큰 포장 연산자의 결과가 유효한 사전 처리기 토큰이 아닌 경우, 토큰 포장 연산자는 불필요하고 해로울 수 있습니다.

예를 들어, 토큰 페이싱 연산자를 사용하여 컴파일 시간에 문자열 리터럴을 작성하려고 할 수 있습니다.

#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 문서화에 관한 장 토큰 이용 운영자에 대한 유용한 정보가 있습니다.

이것은 불필요하게 자신을 반복하지 않기 위해 모든 종류의 상황에서 유용합니다. 다음은 EMACS 소스 코드의 예입니다. 라이브러리에서 여러 기능을로드하고 싶습니다. "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의 이전 질문은 많은 오류가 발생하기 쉬운 retyping없이 열거 상수에 대한 문자열 표현을 생성하는 원활한 방법을 요구했습니다.

링크

그 질문에 대한 나의 대답은 사전 처리기 마술을 적게 적용하면 이와 같이 열거를 정의 할 수있는 방법을 보여주었습니다 (예 : 예를 들어);

ENUM_BEGIN( Color )
  ENUM(RED),
  ENUM(GREEN),
  ENUM(BLUE)
ENUM_END( Color )

... 매크로 확장이 열거 (.H 파일)를 정의 할뿐만 아니라 일치하는 문자열 배열 (.c 파일)도 정의한다는 이점을 얻을 수 있습니다.

const char *ColorStringTable[] =
{
  "RED",
  "GREEN",
  "BLUE"
};

문자열 테이블의 이름은 ## 연산자를 사용하여 매크로 매개 변수 (예 : 색상)를 붙여 넣는 것부터 StringTable로 이루어집니다. 이와 같은 응용 프로그램 (Tricks?)은 # 및 ## 연산자가 매우 귀중한 곳입니다.

나는 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 ##을 기본적으로 기본적으로 퍼지 템플릿에 사용합니다. 기능 오버로드가 없기 때문에 ##은 유형 이름을 생성 된 함수의 이름에 붙이는 데 사용됩니다. 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 


매크로 매개 변수를 다른 것과 연결해야 할 때 토큰 붙여 넣기를 사용할 수 있습니다.

템플릿에 사용할 수 있습니다.

#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;
};

마찬가지로 List Traversal에 대한 함수 템플릿을 작성할 수 있습니다.

매크로로 정의 된 변수에 사용자 정의 접두사를 추가하는 데 사용합니다. 그래서 :

UNITTEST(test_name)

확장 :

void __testframework_test_name ()

주요 용도는 이름 지정 컨벤션이 있고 매크로가 그 이름 지정 컨벤션을 활용하기를 원할 때입니다. 아마도 여러 가지 메소드 제품군이있을 수 있습니다 : image_create (), image_activate () 및 image_release () 또한 file_create (), file_activate (), file_release () 및 mobile_create (), mobile_activate () 및 mobile_activate ().

객체 수명주기를 처리하기 위해 매크로를 작성할 수 있습니다.

#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)

또는 컴파일러가 지원하지 않는 경우 기능 그리고 func:

#define LOG(msg) log_msg(__file__, __line__, ## msg)

위의 "functions"는 메시지를 로그하고 어떤 함수가 메시지를 기록했는지 정확하게 보여줍니다.

내 C ++ 구문이 정확하지 않을 수 있습니다.

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