수색…


소개

X- 매크로는 반복 코드를 최소화하고 데이터 / 코드 일치를 유지하기위한 전처리 기 기반 기술입니다. 하나의 마스터 매크로를 통해 전체 확장 그룹을 표현함으로써 공통 데이터 세트를 기반으로하는 여러 개의 고유 한 매크로 확장이 지원되며, 해당 매크로의 대체 텍스트는 내부 매크로의 확장 시퀀스 (각 데이터마다 하나씩)로 구성됩니다. 내부 매크로는 전통적으로 X() , 따라서 기술의 이름입니다.

비고

X- 매크로 스타일 마스터 매크로의 사용자는 내부 X() 매크로에 대해 자신의 정의를 제공하고 마스터 매크로를 확장하는 범위 내에서 해당 매크로를 정의해야합니다. 따라서 마스터의 내부 매크로 참조는 사용자의 X() 정의에 따라 확장됩니다. 이러한 방식으로 소스 파일의 반복적 인 상용구 코드의 양은 "Do not Repeat Yourself"(DRY) 철학의 지지자가 선호하는 것처럼 X() 의 대체 텍스트에 한 번만 표시 될 수 있습니다.

또한 X() 를 재정의하고 마스터 매크로를 한 번 이상 확장하면 X 매크로는 해당 데이터와 코드를 유지 관리 할 수 ​​있습니다. 매크로를 한 번 확장하면 데이터가 선언됩니다 (예 : 배열 요소 또는 열거 형 멤버). 다른 확장은 해당 코드를 생성합니다.

"X-macro"이름은 내부 매크로의 전통적인 이름에서 유래하지만이 기술은 특정 이름에 의존하지 않습니다. 모든 유효한 매크로 이름을 대신 사용할 수 있습니다.

비판에는 다음이 포함됩니다.

  • X 매크로에 의존하는 소스 파일은 읽기가 더 어렵습니다.
  • 모든 매크로와 마찬가지로 X 매크로는 텍스트 형식이므로 본질적으로 모든 유형의 안전을 제공하지는 않습니다. 과
  • X 매크로는 코드 생성을 제공합니다. 호출 함수에 기반한 대안에 비해 X 매크로는 효과적으로 코드를 크게 만듭니다.

X 매크로에 대한 좋은 설명은 Dr. Dobbs ( http://www.drdobbs.com/the-new-cx-macros/184401387)의 Randy Meyers의 기사 [X-Macros]에서 찾을 수 있습니다.

printfs를위한 X 매크로의 사소한 사용

/* define a list of preprocessor tokens on which to call X */
#define X_123 X(1) X(2) X(3)

/* define X to use */
#define X(val) printf("X(%d) made this print\n", val);
X_123
#undef X
/* good practice to undef X to facilitate reuse later on */

이 예제는 전처리 기가 다음 코드를 생성하게합니다 :

printf("X(%d) made this print\n", 1);
printf("X(%d) made this print\n", 2);
printf("X(%d) made this print\n", 3);

열거 형 값 및 식별자

/* declare items of the enum */
#define FOREACH \
      X(item1) \
      X(item2) \
      X(item3) \
/* end of list */

/* define the enum values */
#define X(id) MyEnum_ ## id,
enum MyEnum { FOREACH };
#undef X

/* convert an enum value to its identifier */
const char * enum2string(int enumValue)
{
    const char* stringValue = NULL;
#define X(id) if (enumValue == MyEnum_ ## id) stringValue = #id;
    FOREACH
#undef X
    return stringValue;
}

다음으로 코드에서 열거 된 값을 사용하여 다음을 사용하여 식별자를 쉽게 인쇄 할 수 있습니다.

printf("%s\n", enum2string(MyEnum_item2));

확장 : X 매크로를 인수로 지정하십시오.

X 매크로 접근법은 "X"매크로의 이름을 마스터 매크로의 인수로 만들어서 약간 일반화 할 수 있습니다. 이것은 매크로 이름 충돌을 피하고 "X"매크로로 범용 매크로를 사용할 수있게하는 이점이 있습니다.

항상 X 매크로와 마찬가지로 마스터 매크로는 해당 매크로에 대한 중요성이있는 항목의 목록을 나타냅니다. 이 변형에서 이와 같은 매크로는 다음과 같이 정의 될 수 있습니다.

/* declare list of items */
#define ITEM_LIST(X) \
      X(item1) \
      X(item2) \
      X(item3) \
/* end of list */

다음과 같이 항목 이름을 인쇄하는 코드를 생성 할 수 있습니다.

/* define macro to apply */
#define PRINTSTRING(value) printf( #value "\n");

/* apply macro to the list of items */
ITEM_LIST(PRINTSTRING)

이 코드는 다음과 같이 확장됩니다.

printf( "item1" "\n"); printf( "item2" "\n"); printf( "item3" "\n");

"X"이름이 마스터 매크로의 내장 된 특성 인 표준 X 매크로와는 달리,이 스타일을 사용하면 나중에 인수 (이 예에서는 PRINTSTRING 로 사용 된 매크로를 정의 해제 할 필요가 없거나 바람직하지 않을 수 있습니다.

코드 생성

X-Macros는 반복적 인 코드를 작성하여 코드 생성, 일부 작업을 수행 할 수있는 목록 반복 또는 상수, 개체 또는 함수 집합을 선언하는 데 사용할 수 있습니다.

여기서 우리는 X 매크로를 사용하여 4 개의 명령과 그 이름의 맵을 문자열로 포함하는 enum을 선언합니다

그런 다음 열거 형의 문자열 값을 인쇄 할 수 있습니다.

/* All our commands */
#define COMMANDS(OP) OP(Open) OP(Close) OP(Save) OP(Quit)

/* generate the enum Commands: {cmdOpen, cmdClose, cmdSave, cmdQuit, }; */
#define ENUM_NAME(name) cmd##name,
enum Commands {
  COMMANDS(ENUM_NAME)
};
#undef ENUM_NAME

/* generate the string table */
#define COMMAND_OP(name) #name,
const char* const commandNames[] = {
  COMMANDS(COMMAND_OP)
};
#undef COMMAND_OP

/* the following prints "Quit\n": */
printf("%s\n", commandNames[cmdQuit]());

비슷하게 우리는 enum 값으로 함수를 호출하는 점프 테이블을 생성 할 수 있습니다.

이렇게하려면 모든 함수가 동일한 서명을 가져야합니다. 인수를 취하지 않고 int를 반환하면 enum 정의가있는 머리글에 다음과 같이 입력합니다.

/* declare all functions as extern */
#define EXTERN_FUNC(name) extern int doCmd##name(void);
COMMANDS(EXTERN_FUNC)
#undef EXTERN_FUNC

/* declare the function pointer type and the jump table  */
typedef int (*CommandFunc)(void);
extern CommandFunc commandJumpTable[];

위의 내용이 헤더로 포함되었다고 가정하면 다음과 같은 컴파일 단위가 다를 수 있습니다.

/* generate the jump table */
#define FUNC_NAME(name) doCmd##name,
CommandFunc commandJumpTable[] = {
  COMMANDS(FUNC_NAME)
};
#undef FUNC_NAME

/* call the save command like this: */
int result = commandJumpTable[cmdSave]();

/* somewhere else, we need the implementations of the commands */
int doCmdOpen(void) {/* code performing open command */}
int doCmdClose(void) {/* code performing close command */}
int doCmdSave(void) {/* code performing save command */}
int doCmdQuit(void) {/* code performing quit command */}

실제 코드에서 사용되는이 기술의 예는 Chromium의 GPU 명령 디스 패칭입니다 .



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow