Sök…


Introduktion

X-makroer är en preprocessor-baserad teknik för att minimera repetitiv kod och upprätthålla data / kodkorrespondenser. Flera distinkta makroutvidgningar baserade på en gemensam uppsättning data stöds genom att representera hela gruppen av utvidgningar via ett enda huvudmakro, med det makroets ersättningstext som består av en sekvens av utvidgningar av en inre makro, en för varje datum. Den inre makroen heter traditionellt X() , följaktligen namnet på tekniken.

Anmärkningar

Användaren av en X-makro-mastermakro förväntas ge sin egen definition för den inre X() -makroen och inom dess ram för att utöka mastermakroet. Befälhavarens inre makroreferenser utvidgas alltså enligt användarens definition av X() . På detta sätt kan mängden repetitiv pannkodskod i källfilen minskas (visas endast en gång, i ersättningstexten för X() ), vilket föredras av anhängare till filosofin "Don't Repeat Yourself" (DRY).

Genom att omdefiniera X() och utöka mastermakroen en eller flera ytterligare gånger kan X-makroner underlätta att bibehålla motsvarande data och kod - en utvidgning av makroen förklarar data (till exempel arrayelement eller enum-medlemmar), och de andra utvidgningarna producerar motsvarande kod.

Även om namnet "X-makro" kommer från det traditionella namnet på den inre makroen, beror inte tekniken på det specifika namnet. Alla giltiga makronamn kan användas på sin plats.

Kritik inkluderar

  • källfiler som förlitar sig på X-makron är svårare att läsa;
  • Liksom alla makron är X-makroer strikt textuella - de ger inte i sig någon typ av säkerhet; och
  • X-makron tillhandahåller kodgenerering . Jämfört med alternativ baserade på samtalsfunktioner, gör X-makron effektivt koden större.

En bra förklaring av X-makron finns i Randy Meyers artikel [X-Macros] i Dr. Dobbs ( http://www.drdobbs.com/the-new-cx-macros/184401387) .

Trivial användning av X-makron för printfs

/* 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 */

Detta exempel kommer att resultera i att förarbetaren genererar följande kod:

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

Enumvärde och identifierare

/* 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;
}

Därefter kan du använda det uppräknade värdet i din kod och enkelt skriva ut dess identifierare med:

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

Tillägg: Ge X-makro som ett argument

X-makro-metoden kan generaliseras lite genom att göra namnet på "X" -makroen till ett argument för huvudmakro. Detta har fördelarna med att undvika kollisioner i makronamn och att tillåta användning av ett allmänt makro som "X" -makro.

Som alltid med X-makroer representerar huvudmakroen en lista med objekt vars betydelse är specifik för den makroen. I denna variation kan en sådan makro definieras så:

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

Man kan då generera kod för att skriva ut artikelnamnen så:

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

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

Som expanderar till denna kod:

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

I motsats till vanliga X-makron, där "X" -namnet är en inbyggd egenskap hos mastermakroen, med denna stil kan det vara onödigt eller till och med oönskat att i efterhand undefiniera makroen som används som argument ( PRINTSTRING i detta exempel).

Kodgenerering

X-makron kan användas för kodgenerering, genom att skriva upprepande kod: iterera över en lista för att utföra vissa uppgifter, eller för att förklara en uppsättning konstanter, objekt eller funktioner.

Här använder vi X-makron för att förklara en enum som innehåller fyra kommandon och en karta över deras namn som strängar

Då kan vi skriva ut strängvärdena för 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]());

På liknande sätt kan vi generera en hopptabell för att ringa funktioner med enumvärdet.

Detta kräver att alla funktioner har samma signatur. Om de inte tar några argument och returnerar ett int, skulle vi lägga detta i en rubrik med definitionen 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[];

Allt följande kan finnas i olika sammanställningsenheter förutsatt att delen ovan ingår som en rubrik:

/* 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 */}

Ett exempel på denna teknik som används i riktig kod är för GPU-kommandosändning i Chromium .



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow