development

## 전 처리기 연산자의 응용 프로그램과 고려해야 할 사항은 무엇입니까?

big-blog 2020. 9. 14. 21:28
반응형

## 전 처리기 연산자의 응용 프로그램과 고려해야 할 사항은 무엇입니까?


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

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

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

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

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


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

CrashRpt (충돌보고 라이브러리)의 흥미로운 사용법은 다음과 같습니다.

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

날짜와 시간이 포함 된 문자열을 반환하는 다른 매크로와 함께 사용합니다.

퍼팅 LA를 다음 것은 __ 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

다음은 새 버전의 컴파일러로 업그레이드 할 때 발생한 문제입니다.

Unnecessary use of the token-pasting operator (##) is non-portable and may generate undesired whitespace, warnings, or errors.

When the result of the token-pasting operator is not a valid preprocessor token, the token-pasting operator is unnecessary and possibly harmful.

For example, one might try to build string literals at compile time using the token-pasting operator:

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

On some compilers, this will output the expected result:

1+2 std::vector

On other compilers, this will include undesired whitespace:

1 + 2 std :: vector

Fairly modern versions of GCC (>=3.3 or so) will fail to compile this code:

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

The solution is to omit the token-pasting operator when concatenating preprocessor tokens to C/C++ operators:

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

The GCC CPP documentation chapter on concatenation has more useful information on the token-pasting operator.


This is useful in all kinds of situations in order not to repeat yourself needlessly. The following is an example from the Emacs source code. We would like to load a number of functions from a library. The function "foo" should be assigned to fn_foo, and so on. We define the following macro:

#define LOAD_IMGLIB_FN(lib,func) {                                      \
    fn_##func = (void *) GetProcAddress (lib, #func);                   \
    if (!fn_##func) return 0;                                           \
  }

We can then use it:

LOAD_IMGLIB_FN (library, XpmFreeAttributes);
LOAD_IMGLIB_FN (library, XpmCreateImageFromBuffer);
LOAD_IMGLIB_FN (library, XpmReadFileToImage);
LOAD_IMGLIB_FN (library, XImageFree);

The benefit is not having to write both fn_XpmFreeAttributes and "XpmFreeAttributes" (and risk misspelling one of them).


A previous question on Stack Overflow asked for a smooth method of generating string representations for enumeration constants without a lot of error-prone retyping.

Link

My answer to that question showed how applying little preprocessor magic lets you define your enumeration like this (for example) ...;

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

... With the benefit that the macro expansion not only defines the enumeration (in a .h file), it also defines a matching array of strings (in a .c file);

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

The name of the string table comes from pasting the macro parameter (i.e. Color) to StringTable using the ## operator. Applications (tricks?) like this are where the # and ## operators are invaluable.


You can use token pasting when you need to concatenate macro parameters with something else.

It can be used for templates:

#define LINKED_LIST(A) struct list##_##A {\
A value; \
struct list##_##A *next; \
};

In this case LINKED_LIST(int) would give you

struct list_int {
int value;
struct list_int *next;
};

Similarly you can write a function template for list traversal.


I use it in C programs to help correctly enforce the prototypes for a set of methods that must conform to some sort of calling convention. In a way, this can be used for poor man's object orientation in straight C:

SCREEN_HANDLER( activeCall )

expands to something like this:

STATUS activeCall_constructor( HANDLE *pInst )
STATUS activeCall_eventHandler( HANDLE *pInst, TOKEN *pEvent );
STATUS activeCall_destructor( HANDLE *pInst );

This enforces correct parameterization for all "derived" objects when you do:

SCREEN_HANDLER( activeCall )
SCREEN_HANDLER( ringingCall )
SCREEN_HANDLER( heldCall )

the above in your header files, etc. It is also useful for maintenance if you even happen to want to change the definitions and/or add methods to the "objects".


SGlib uses ## to basically fudge templates in C. Because there's no function overloading, ## is used to glue the type name into the names of the generated functions. If I had a list type called list_t, then I would get functions named like sglib_list_t_concat, and so on.


I use it for a home rolled assert on a non-standard C compiler for embedded:



#define ASSERT(exp) if(!(exp)){ \
                      print_to_rs232("Assert failed: " ## #exp );\
                      while(1){} //Let the watchdog kill us 



I use it for adding custom prefixes to variables defined by macros. So something like:

UNITTEST(test_name)

expands to:

void __testframework_test_name ()

The main use is when you have a naming convention and you want your macro to take advantage of that naming convention. Perhaps you have several families of methods: image_create(), image_activate(), and image_release() also file_create(), file_activate(), file_release(), and mobile_create(), mobile_activate() and mobile_release().

You could write a macro for handling object lifecycle:

#define LIFECYCLE(name, func) (struct name x = name##_create(); name##_activate(x); func(x); name##_release())

Of course, a sort of "minimal version of objects" is not the only sort of naming convention this applies to -- nearly the vast majority of naming conventions make use of a common sub-string to form the names. It could me function names (as above), or field names, variable names, or most anything else.


One important use in WinCE:

#define BITFMASK(bit_position) (((1U << (bit_position ## _WIDTH)) - 1) << (bit_position ## _LEFTSHIFT))

While defining register bit description we do following:

#define ADDR_LEFTSHIFT                          0

#define ADDR_WIDTH                              7

And while using BITFMASK, simply use:

BITFMASK(ADDR)

It is very useful for logging. You can do:

#define LOG(msg) log_msg(__function__, ## msg)

Or, if your compiler doesn't support function and func:

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

The above "functions" logs message and shows exactly which function logged a message.

My C++ syntax might be not quite correct.

참고URL : https://stackoverflow.com/questions/216875/what-are-the-applications-of-the-preprocessor-operator-and-gotchas-to-conside

반응형