itsource

X-Macros의 실제 사용

mycopycode 2022. 9. 23. 00:16
반응형

X-Macros의 실제 사용

X-Macros를 방금 배웠어요.X-Macros의 실제 용도는 무엇입니까?언제가 그 일에 적합한 도구입니까?

몇 년 전에 X-macros를 발견했는데, 그때 코드 안에 함수 포인터를 사용하기 시작했습니다.저는 임베디드 프로그래머로 스테이트 머신을 자주 사용합니다.나는 종종 다음과 같은 코드를 작성하곤 했다.

/* declare an enumeration of state codes */
enum{ STATE0, STATE1, STATE2, ... , STATEX, NUM_STATES};

/* declare a table of function pointers */
p_func_t jumptable[NUM_STATES] = {func0, func1, func2, ... , funcX};

문제는 상태 열거의 순서와 일치하도록 함수 포인터 테이블의 순서를 유지해야 하는 매우 오류가 발생하기 쉽다고 생각한다는 것입니다.

친구가 엑스매크로스를 소개해줬는데 머릿속에서 전구가 터지는 것 같았어요.진짜, 내 평생 어디 있었어?

여기서 다음 표를 정의합니다.

#define STATE_TABLE \
        ENTRY(STATE0, func0) \
        ENTRY(STATE1, func1) \
        ENTRY(STATE2, func2) \
        ...
        ENTRY(STATEX, funcX) \

그리고 다음과 같이 사용할 수 있습니다.

enum
{
#define ENTRY(a,b) a,
    STATE_TABLE
#undef ENTRY
    NUM_STATES
};

그리고.

p_func_t jumptable[NUM_STATES] =
{
#define ENTRY(a,b) b,
    STATE_TABLE
#undef ENTRY
};

또한 프리프로세서가 다음과 같이 기능 프로토타입을 제작하도록 할 수도 있습니다.

#define ENTRY(a,b) static void b(void);
    STATE_TABLE
#undef ENTRY

또 다른 용도는 레지스터를 선언하고 초기화하는 것입니다.

#define IO_ADDRESS_OFFSET (0x8000)
#define REGISTER_TABLE\
    ENTRY(reg0, IO_ADDRESS_OFFSET + 0, 0x11)\
    ENTRY(reg1, IO_ADDRESS_OFFSET + 1, 0x55)\
    ENTRY(reg2, IO_ADDRESS_OFFSET + 2, 0x1b)\
    ...
    ENTRY(regX, IO_ADDRESS_OFFSET + X, 0x33)\

/* declare the registers (where _at_ is a compiler specific directive) */
#define ENTRY(a, b, c) volatile uint8_t a _at_ b:
    REGISTER_TABLE
#undef ENTRY

/* initialize registers */
#define ENTRY(a, b, c) a = c;
    REGISTER_TABLE
#undef ENTRY

그러나 내가 가장 좋아하는 용도는 통신 핸들러에 관한 것이다.

먼저 각 명령 이름과 코드를 포함하는 Comms 테이블을 만듭니다.

#define COMMAND_TABLE \
    ENTRY(RESERVED,    reserved,    0x00) \
    ENTRY(COMMAND1,    command1,    0x01) \
    ENTRY(COMMAND2,    command2,    0x02) \
    ...
    ENTRY(COMMANDX,    commandX,    0x0X) \

대소문자는 enum, 소문자는 함수 이름에 사용되므로 테이블에는 대소문자가 모두 표시됩니다.

그런 다음 각 명령어의 구조를 정의하여 각 명령어의 모양을 정의합니다.

typedef struct {...}command1_cmd_t;
typedef struct {...}command2_cmd_t;

etc.

마찬가지로 각 명령 응답에 대한 구조를 정의합니다.

typedef struct {...}command1_resp_t;
typedef struct {...}command2_resp_t;

etc.

다음으로 명령어 코드 열거를 정의할 수 있습니다.

enum
{
#define ENTRY(a,b,c) a##_CMD = c,
    COMMAND_TABLE
#undef ENTRY
};

명령어 길이 열거를 정의할 수 있습니다.

enum
{
#define ENTRY(a,b,c) a##_CMD_LENGTH = sizeof(b##_cmd_t);
    COMMAND_TABLE
#undef ENTRY
};

응답 길이 열거를 정의할 수 있습니다.

enum
{
#define ENTRY(a,b,c) a##_RESP_LENGTH = sizeof(b##_resp_t);
    COMMAND_TABLE
#undef ENTRY
};

다음과 같은 명령어가 몇 개 있는지 확인할 수 있습니다.

typedef struct
{
#define ENTRY(a,b,c) uint8_t b;
    COMMAND_TABLE
#undef ENTRY
} offset_struct_t;

#define NUMBER_OF_COMMANDS sizeof(offset_struct_t)

메모: 실제로 offset_struct_t를 인스턴스화한 적이 없습니다.단, 컴파일러가 명령어 수 정의를 생성하는 방법으로 사용할 뿐입니다.

그러면 다음과 같이 함수 포인터의 테이블을 생성할 수 있습니다.

p_func_t jump_table[NUMBER_OF_COMMANDS] = 
{
#define ENTRY(a,b,c) process_##b,
    COMMAND_TABLE
#undef ENTRY
}

기능 프로토타입은 다음과 같습니다.

#define ENTRY(a,b,c) void process_##b(void);
    COMMAND_TABLE
#undef ENTRY

마지막으로, 송신 버퍼의 사이즈를 컴파일러에 계산하도록 하겠습니다.

/* reminder the sizeof a union is the size of its largest member */
typedef union
{
#define ENTRY(a,b,c) uint8_t b##_buf[sizeof(b##_cmd_t)];
    COMMAND_TABLE
#undef ENTRY
}tx_buf_t

이 결합은 오프셋 구조와 같으며 인스턴스화되지 않습니다.대신 size of operator를 사용하여 송신 버퍼 크기를 선언할 수 있습니다.

uint8_t tx_buf[sizeof(tx_buf_t)];

현재 송신 버퍼 tx_buf는 최적의 크기이며 명령어를 이 통신 핸들러에 추가할 때 버퍼는 항상 최적의 크기가 됩니다.그러자!

다른 용도는 오프셋 테이블을 작성하는 것입니다.메모리가 임베디드 시스템의 제약이 되는 경우가 많기 때문에 스퍼스 배열일 경우 점프 테이블(포인터당 2바이트 X 256 명령 가능)에 512바이트를 사용하고 싶지 않습니다.대신 가능한 각 명령어에 대해 8비트 오프셋 표가 있습니다.이 오프셋은 NUM_COMMANDs * size of (pointinter)만 필요한 실제 점프 테이블에 인덱싱하기 위해 사용됩니다.제 경우 10개의 명령어가 정의되어 있습니다.점프 테이블은 20바이트 길이이고 오프셋 테이블은 256바이트로 512바이트가 아닌 총 276바이트입니다.그런 다음 다음과 같이 기능을 호출합니다.

jump_table[offset_table[command]]();

대신

jump_table[command]();

다음과 같이 오프셋 테이블을 만들 수 있습니다.

/* initialize every offset to 0 */
static uint8_t offset_table[256] = {0};

/* for each valid command, initialize the corresponding offset */
#define ENTRY(a,b,c) offset_table[c] = offsetof(offset_struct_t, b);
    COMMAND_TABLE
#undef ENTRY

여기서 offsetof는 stdef에서 정의된 표준 라이브러리 매크로입니다.h"

또, 커맨드 코드가 서포트되고 있는지 아닌지를 판별하는 간단한 방법이 있습니다.

bool command_is_valid(uint8_t command)
{
    /* return false if not valid, or true (non 0) if valid */
    return offset_table[command];
}

이것이 내 COMMAND_TAB에 있는 이유이기도 합니다.LE I은 명령어바이트 0을 예약했습니다."process_reserved()"라는 함수를 하나 만들 수 있습니다.이 함수는 비활성 명령어바이트를 사용하여 오프셋테이블에 인덱스를 붙이면 호출됩니다.

X-Macros는 기본적으로 매개 변수화된 템플릿입니다.따라서 여러 가지 상황에서 유사한 것이 필요한 경우 작업에 적합한 도구입니다.이를 통해 추상 양식을 생성하고 다른 규칙에 따라 인스턴스화할 수 있습니다.

X 매크로를 사용하여 열거값을 문자열로 출력합니다.그 후, 각 요소에 「사용자」매크로가 적용되는 이 폼을 매우 선호합니다.여러 파일을 포함하면 작업하기가 훨씬 더 어렵습니다.

/* x-macro constructors for error and type
   enums and string tables */
#define AS_BARE(a) a ,
#define AS_STR(a) #a ,

#define ERRORS(_) \
    _(noerror) \
    _(dictfull) _(dictstackoverflow) _(dictstackunderflow) \
    _(execstackoverflow) _(execstackunderflow) _(limitcheck) \
    _(VMerror)
enum err { ERRORS(AS_BARE) };
char *errorname[] = { ERRORS(AS_STR) };
/* puts(errorname[(enum err)limitcheck]); */

오브젝트 타입에 따른 기능 디스패치에도 사용하고 있습니다.다시 한 번 열거값을 만들 때 사용한 매크로를 가로채고 있습니다.

#define TYPES(_) \
    _(invalid) \
    _(null) \
    _(mark) \
    _(integer) \
    _(real) \
    _(array) \
    _(dict) \
    _(save) \
    _(name) \
    _(string) \
/*enddef TYPES */

#define AS_TYPE(_) _ ## type ,
enum { TYPES(AS_TYPE) };

매크로를 사용하면 모든 배열 인덱스가 연관된 열거값과 일치합니다. 매크로 정의(TYPE 매크로)의 베어 토큰을 사용하여 다양한 형식을 구성하기 때문입니다.

typedef void evalfunc(context *ctx);

void evalquit(context *ctx) { ++ctx->quit; }

void evalpop(context *ctx) { (void)pop(ctx->lo, adrent(ctx->lo, OS)); }

void evalpush(context *ctx) {
    push(ctx->lo, adrent(ctx->lo, OS),
            pop(ctx->lo, adrent(ctx->lo, ES)));
}

evalfunc *evalinvalid = evalquit;
evalfunc *evalmark = evalpop;
evalfunc *evalnull = evalpop;
evalfunc *evalinteger = evalpush;
evalfunc *evalreal = evalpush;
evalfunc *evalsave = evalpush;
evalfunc *evaldict = evalpush;
evalfunc *evalstring = evalpush;
evalfunc *evalname = evalpush;

evalfunc *evaltype[stringtype/*last type in enum*/+1];
#define AS_EVALINIT(_) evaltype[_ ## type] = eval ## _ ;
void initevaltype(void) {
    TYPES(AS_EVALINIT)
}

void eval(context *ctx) {
    unsigned ades = adrent(ctx->lo, ES);
    object t = top(ctx->lo, ades, 0);
    if ( isx(t) ) /* if executable */
        evaltype[type(t)](ctx);  /* <--- the payoff is this line here! */
    else
        evalpush(ctx);
}

이 방법으로 X-macros를 사용하면 컴파일러가 유용한 오류 메시지를 표시할 수 있습니다.위의 evalarray 함수는 제 요점에서 산만해지기 때문에 생략했습니다.그러나 위의 코드를 컴파일하려고 하면(다른 함수 호출을 코멘트하고 컨텍스트에 더미의 typedef를 제공), 컴파일러는 함수 누락에 대해 불만을 제기합니다.새로 추가할 때마다 이 모듈을 다시 컴파일할 때 핸들러를 추가하라는 메시지가 표시됩니다.따라서 X-macro는 프로젝트가 성장하더라도 병렬 구조가 그대로 유지되도록 보장합니다.

편집:

이 답변은 나의 평판을 50% 상승시켰다.그래서 여기 조금 더 있습니다.다음은 X-Macros를 사용하지 않는 경우질문에 대한 부정적인 입니다.

이 예에서는 임의의 코드 fragment를 X-"record"로 패킹하는 방법을 보여 줍니다.저는 결국 이 프로젝트의 분과를 포기했고, 이후 설계에서는 이 전략을 사용하지 않았습니다(또한 시도하지 않았기 때문은 아닙니다.어쩐지 몸이 안 좋아졌어요.확실히 매크로의 이름은 X6인데, 그 이유는 한때 6개의 인수가 있었기 때문입니다만, 매크로명을 변경하는 것에 싫증이 났습니다.

/* Object types */
/* "'X'" macros for Object type definitions, declarations and initializers */
// a                      b            c              d
// enum,                  string,      union member,  printf d
#define OBJECT_TYPES \
X6(    nulltype,        "null",     int dummy      ,            ("<null>")) \
X6(    marktype,        "mark",     int dummy2      ,           ("<mark>")) \
X6( integertype,     "integer",     int  i,     ("%d",o.i)) \
X6( booleantype,     "boolean",     bool b,     (o.b?"true":"false")) \
X6(    realtype,        "real",     float f,        ("%f",o.f)) \
X6(    nametype,        "name",     int  n,     ("%s%s", \
        (o.flags & Fxflag)?"":"/", names[o.n])) \
X6(  stringtype,      "string",     char *s,        ("%s",o.s)) \
X6(    filetype,        "file",     FILE *file,     ("<file %p>",(void *)o.file)) \
X6(   arraytype,       "array",     Object *a,      ("<array %u>",o.length)) \
X6(    dicttype,        "dict",     struct s_pair *d, ("<dict %u>",o.length)) \
X6(operatortype,    "operator",     void (*o)(),    ("<op>")) \

#define X6(a, b, c, d) #a,
char *typestring[] = { OBJECT_TYPES };
#undef X6

// the Object type
//forward reference so s_object can contain s_objects
typedef struct s_object Object;

// the s_object structure:
// a bit convoluted, but it boils down to four members:
// type, flags, length, and payload (union of type-specific data)
// the first named union member is integer, so a simple literal object
// can be created on the fly:
// Object o = {integertype,0,0,4028}; //create an int object, value: 4028
// Object nl = {nulltype,0,0,0};
struct s_object {
#define X6(a, b, c, d) a,
    enum e_type { OBJECT_TYPES } type;
#undef X6
unsigned int flags;
#define Fread  1
#define Fwrite 2
#define Fexec  4
#define Fxflag 8
size_t length; //for lint, was: unsigned int
#define X6(a, b, c, d) c;
    union { OBJECT_TYPES };
#undef X6
};

한 가지 큰 문제는 printf 형식의 문자열이었습니다.멋있어 보이긴 하지만 그냥 호커스일 뿐이야하나의 기능에서만 사용되기 때문에 매크로를 과도하게 사용하면 실제로 함께 있어야 할 정보가 분리되어 기능 자체를 읽을 수 없게 됩니다.이와 같은 디버깅 함수에서는 난독화가 이중으로 불운합니다.

//print the object using the type's format specifier from the macro
//used by O_equal (ps: =) and O_equalequal (ps: ==)
void printobject(Object o) {
    switch (o.type) {
#define X6(a, b, c, d) \
        case a: printf d; break;
OBJECT_TYPES
#undef X6
    }
}

그러니 너무 흥분하지 마세요.내가 했던 것처럼.

저는 상당히 큰 X-매크로를 사용하여 INI 파일의 콘텐츠를 구성 구조에 로드합니다. 특히 이 구조를 중심으로 회전합니다.

"configuration.def" 파일은 다음과 같습니다.

#define NMB_DUMMY(...) X(__VA_ARGS__)
#define NMB_INT_DEFS \
   TEXT("long int") , long , , , GetLongValue , _ttol , NMB_SECT , SetLongValue , 

#define NMB_STR_DEFS NMB_STR_DEFS__(TEXT("string"))
#define NMB_PATH_DEFS NMB_STR_DEFS__(TEXT("path"))

#define NMB_STR_DEFS__(ATYPE) \
  ATYPE ,  basic_string<TCHAR>* , new basic_string<TCHAR>\
  , delete , GetValue , , NMB_SECT , SetValue , *

/* X-macro starts here */

#define NMB_SECT "server"
NMB_DUMMY(ip,TEXT("Slave IP."),TEXT("10.11.180.102"),NMB_STR_DEFS)
NMB_DUMMY(port,TEXT("Slave portti."),TEXT("502"),NMB_STR_DEFS)
NMB_DUMMY(slaveid,TEXT("Slave protocol ID."),0xff,NMB_INT_DEFS)
.
. /* And so on for about 40 items. */

좀 헷갈리긴 하지만 인정해요모든 필드 매크로 뒤에 실제로 모든 유형의 선언문을 쓰고 싶지 않다는 것이 곧 분명해졌다. (걱정하지 마세요. 간결하게 설명해야 할 큰 코멘트가 있습니다.)

구성 구조를 선언하는 방법은 다음과 같습니다.

typedef struct {
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,...) TYPE ID;
#include "configuration.def"
#undef X
  basic_string<TCHAR>* ini_path;  //Where all the other stuff gets read.
  long verbosity;                 //Used only by console writing functions.
} Config;

다음으로 코드에서 디폴트값이 먼저 Configuration구조에 읽힙니다.

#define X(ID,DESC,DEFVAL,ATYPE,TYPE,CONSTRUCTOR,DESTRUCTOR,GETTER,STRCONV,SECT,SETTER,...) \
  conf->ID = CONSTRUCTOR(DEFVAL);
#include "configuration.def"
#undef X

그런 다음 라이브러리 Simple을 사용하여 다음과 같이 INI를 구성 구조로 읽어 들입니다.이니:

#define X(ID,DESC,DEFVAL,ATYPE,TYPE,CONSTRUCTOR,DESTRUCTOR,GETTER,STRCONV,SECT,SETTER,DEREF...)\
  DESTRUCTOR (conf->ID);\
  conf->ID  = CONSTRUCTOR( ini.GETTER(TEXT(SECT),TEXT(#ID),DEFVAL,FALSE) );\
  LOG3A(<< left << setw(13) << TEXT(#ID) << TEXT(": ")  << left << setw(30)\
    << DEREF conf->ID << TEXT(" (") << DEFVAL << TEXT(").") );
#include "configuration.def"
#undef X

또한 동일한 이름(GNU 긴 형식)으로 포맷된 명령줄 플래그로부터의 오버라이드는 라이브러리 SimpleOpt를 사용하여 다음과 같이 적용됩니다.

enum optflags {
#define X(ID,...) ID,
#include "configuration.def"
#undef X
  };
  CSimpleOpt::SOption sopt[] = {
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,...) {ID,TEXT("--") #ID TEXT("="), SO_REQ_CMB},
#include "configuration.def"
#undef X
    SO_END_OF_OPTIONS
  };
  CSimpleOpt ops(argc,argv,sopt,SO_O_NOERR);
  while(ops.Next()){
    switch(ops.OptionId()){
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,CONSTRUCTOR,DESTRUCTOR,GETTER,STRCONV,SECT,...) \
  case ID:\
    DESTRUCTOR (conf->ID);\
    conf->ID = STRCONV( CONSTRUCTOR (  ops.OptionArg() ) );\
    LOG3A(<< TEXT("Omitted ")<<left<<setw(13)<<TEXT(#ID)<<TEXT(" : ")<<conf->ID<<TEXT(" ."));\
    break;
#include "configuration.def"
#undef X
    }
  }

또한 동일한 매크로를 사용하여 --help -flag 출력을 출력하고 샘플 기본 ini 파일 configuration.def가 프로그램에 8번 포함되어 있습니다."둥근 구멍에 사각 못을 박는다"는 식으로, 실제로 유능한 프로그래머가 이 작업을 어떻게 진행할 수 있을까요?많은 루프와 문자열 처리?

인기 있는 대규모 프로젝트에서 X-Macros를 실제로 사용하는 방법:

자바 핫스팟

Oracle HotSpot Virtual Machine for Java® Programming Language에 다음 파일이 있습니다.globals.hpp, 를 사용합니다.RUNTIME_FLAGS그런 식으로.

소스코드를 참조해 주세요.

크롬

net_error_list.h의 네트워크 오류 목록은 다음 형식의 매크로 확장 목록입니다.

NET_ERROR(IO_PENDING, -1)

같은 디렉토리의 net_errors.h에 의해 사용됩니다.

enum Error {
  OK = 0,

#define NET_ERROR(label, value) ERR_ ## label = value,
#include "net/base/net_error_list.h"
#undef NET_ERROR
};

이 프리프로세서 매직의 결과는 다음과 같습니다.

enum Error {
  OK = 0,
  ERR_IO_PENDING = -1,
};

이 특별한 용도가 마음에 들지 않는 것은 상수의 이름이 다음 값을 추가하여 동적으로 생성된다는 것입니다.ERR_이 예에서는,NET_ERROR(IO_PENDING, -100)상수를 정의합니다.ERR_IO_PENDING.

단순 텍스트 검색 사용:ERR_IO_PENDING이 상수가 정의된 위치를 확인할 수 없습니다.대신 정의를 찾으려면 다음을 검색해야 합니다.IO_PENDING이로 인해 코드 탐색이 어려워지고 코드 기반 전체가 난독화됩니다.

X 매크로를 사용하여 열거값 반복과 각 열거값 문자열 표현을 지원하는 '리치 열거'를 만들고 싶습니다.

#define MOUSE_BUTTONS \
X(LeftButton, 1)   \
X(MiddleButton, 2) \
X(RightButton, 4)

struct MouseButton {
  enum Value {
    None = 0
#define X(name, value) ,name = value
MOUSE_BUTTONS
#undef X
  };

  static const int *values() {
    static const int a[] = {
      None,
#define X(name, value) name,
    MOUSE_BUTTONS
#undef X
      -1
    };
    return a;
  }

  static const char *valueAsString( Value v ) {
#define X(name, value) static const char str_##name[] = #name;
MOUSE_BUTTONS
#undef X
    switch ( v ) {
      case None: return "None";
#define X(name, value) case name: return str_##name;
MOUSE_BUTTONS
#undef X
    }
    return 0;
  }
};

이는 단순히 정의되는 것이 아닙니다.MouseButton::Valueenum, 이것은 또한 내가 이런 것들을 할 수 있게 해준다.

// Print names of all supported mouse buttons
for ( const int *mb = MouseButton::values(); *mb != -1; ++mb ) {
    std::cout << MouseButton::valueAsString( (MouseButton::Value)*mb ) << "\n";
}

https://github.com/whunmr/DataEx

serialize 및 deserialize 기능이 내장된 C++ 클래스를 생성하기 위해 다음 xmacros를 사용하고 있습니다.

#define __FIELDS_OF_DataWithNested(_)  \
  _(1, a, int  )                       \
  _(2, x, DataX)                       \
  _(3, b, int  )                       \
  _(4, c, char )                       \
  _(5, d, __array(char, 3))            \
  _(6, e, string)                      \
  _(7, f, bool)

DEF_DATA(DataWithNested);

사용방법:

TEST_F(t, DataWithNested_should_able_to_encode_struct_with_nested_struct) {
    DataWithNested xn;
    xn.a = 0xCAFEBABE;
    xn.x.a = 0x12345678;
    xn.x.b = 0x11223344;
    xn.b = 0xDEADBEEF;
    xn.c = 0x45;
    memcpy(&xn.d, "XYZ", strlen("XYZ"));

    char buf_with_zero[] = {0x11, 0x22, 0x00, 0x00, 0x33};
    xn.e = string(buf_with_zero, sizeof(buf_with_zero));
    xn.f = true;

    __encode(DataWithNested, xn, buf_);

    char expected[] = { 0x01, 0x04, 0x00, 0xBE, 0xBA, 0xFE, 0xCA,
                        0x02, 0x0E, 0x00 /*T and L of nested X*/,
                        0x01, 0x04, 0x00, 0x78, 0x56, 0x34, 0x12,
                        0x02, 0x04, 0x00, 0x44, 0x33, 0x22, 0x11,
                        0x03, 0x04, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
                        0x04, 0x01, 0x00, 0x45,
                        0x05, 0x03, 0x00, 'X', 'Y', 'Z',
                        0x06, 0x05, 0x00, 0x11, 0x22, 0x00, 0x00, 0x33,
                        0x07, 0x01, 0x00, 0x01};

    EXPECT_TRUE(ArraysMatch(expected, buf_));
}

또 다른 예는 https://github.com/whunmr/msgrpc에 있습니다.

크롬은 dom_code_data.inc에서 X매크로의 흥미로운 변형을 가지고 있다.단순한 매크로가 아니라 완전히 다른 파일이라는 것만 빼면요.이 파일은 서로 다른 플랫폼의 스캔 코드, USB HID 코드 및 문자열 같은 이름 간의 키보드 입력 매핑을 위한 것입니다.

파일에는 다음과 같은 코드가 포함되어 있습니다.

DOM_CODE_DECLARATION {

  //            USB     evdev    XKB     Win     Mac   Code
  DOM_CODE(0x000000, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NONE), // Invalid
...
};

각 매크로 호출은 실제로 7개의 인수로 전달되며 매크로에서는 사용할 인수와 무시할 인수를 선택할 수 있습니다.OS 키 코드와 플랫폼에 의존하지 않는 스캔 코드 및 DOM 문자열 간에 매핑하는 방법이 있습니다.OS마다 다른 매크로를 사용하여 해당 OS에 적합한 키 코드를 선택합니다.

// Table of USB codes (equivalent to DomCode values), native scan codes,
// and DOM Level 3 |code| strings.
#if defined(OS_WIN)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
  { usb, win, code }
#elif defined(OS_LINUX)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
  { usb, xkb, code }
#elif defined(OS_MACOSX)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
  { usb, mac, code }
#elif defined(OS_ANDROID)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
  { usb, evdev, code }
#else
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
  { usb, 0, code }
#endif
#define DOM_CODE_DECLARATION const KeycodeMapEntry usb_keycode_map[] =
#include "ui/events/keycodes/dom/dom_code_data.inc"
#undef DOM_CODE
#undef DOM_CODE_DECLARATION

언급URL : https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros

반응형