Suche…


Einführung

X-Makros sind eine auf dem Präprozessor basierende Technik zum Minimieren von Wiederholungscode und zum Verwalten von Daten- / Code-Entsprechungen. Mehrere verschiedene Makroerweiterungen, die auf einem gemeinsamen Datensatz basieren, werden unterstützt, indem die gesamte Erweiterungsgruppe über ein einziges Master-Makro dargestellt wird. Der Ersetzungstext dieses Makros besteht aus einer Folge von Erweiterungen eines inneren Makros, einer für jedes Datum. Das innere Makro wird traditionell X() , daher der Name der Technik.

Bemerkungen

Es wird erwartet, dass der Benutzer eines X-Makro-Master-Makros seine eigene Definition für das innere X() -Makro bereitstellt und in seinem Bereich das Master-Makro erweitert. Die inneren Makroreferenzen des Masters werden entsprechend der Definition des Benutzers von X() . Auf diese Weise kann die Menge des sich wiederholenden Boilerplate-Codes in der Quelldatei reduziert werden (erscheint nur einmal im Ersetzungstext von X() ), wie es von Anhängern der "Do not Repeat Yourself" (DRY) Philosophie bevorzugt wird.

Darüber hinaus können X-Makros durch das erneute Definieren von X() und Erweitern des Master-Makros ein oder mehrere zusätzliche Male die Pflege entsprechender Daten und Code erleichtern - eine Erweiterung des Makros deklariert die Daten (beispielsweise als Array-Elemente oder Enum-Member) und Die anderen Erweiterungen erzeugen entsprechenden Code.

Obwohl der Name "X-Makro" vom traditionellen Namen des inneren Makros stammt, hängt die Technik nicht von diesem Namen ab. Jeder gültige Makroname kann an seiner Stelle verwendet werden.

Kritikpunkte umfassen

  • Quelldateien, die X-Makros verwenden, sind schwieriger zu lesen.
  • Wie alle Makros sind X-Makros streng textlich - sie bieten von Natur aus keine Typensicherheit. und
  • X-Makros ermöglichen die Codegenerierung . Im Vergleich zu auf Aufruffunktionen basierenden Alternativen wird der Code durch X-Makros effektiv vergrößert.

Eine gute Erklärung für X-Makros finden Sie in Randy Meyers 'Artikel [X-Macros] in Dr. Dobbs ( http://www.drdobbs.com/the-new-cx-macros/184401387) .

Trivialer Einsatz von X-Makros 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 */

In diesem Beispiel generiert der Präprozessor den folgenden Code:

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

Aufzählungswert und Bezeichner

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

Als Nächstes können Sie den Aufzählungswert in Ihrem Code verwenden und den Bezeichner auf folgende Weise drucken:

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

Erweiterung: Geben Sie das X-Makro als Argument an

Der X-Makro-Ansatz kann etwas verallgemeinert werden, indem der Name des Makros "X" zu einem Argument des Master-Makros gemacht wird. Dies hat den Vorteil, dass Sie Makronamenskollisionen vermeiden und die Verwendung eines Allzweckmakros als "X" -Makro zulassen.

Wie bei X-Makros stellt das Master-Makro eine Liste von Elementen dar, deren Bedeutung für dieses Makro spezifisch ist. In dieser Variante kann ein solches Makro folgendermaßen definiert werden:

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

Man könnte dann Code generieren, um die Artikelnamen wie folgt auszudrucken:

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

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

Das erweitert sich zu diesem Code:

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

Im Gegensatz zu Standard-X-Makros, bei denen der Name "X" ein integriertes Merkmal des Master-Makros ist, kann es bei diesem Stil unnötig oder sogar unerwünscht sein, das als Argument verwendete Makro (in diesem Beispiel PRINTSTRING ) PRINTSTRING .

Codegenerierung

X-Makros können zur Codegenerierung verwendet werden, indem wiederholter Code geschrieben wird: Durchlaufen Sie eine Liste, um bestimmte Aufgaben auszuführen oder eine Reihe von Konstanten, Objekten oder Funktionen zu deklarieren.

Hier verwenden wir X-Makros, um eine Aufzählung mit 4 Befehlen und eine Zuordnung ihrer Namen als Zeichenfolgen zu deklarieren

Dann können wir die Zeichenfolgewerte der Aufzählung drucken.

/* 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]());

In ähnlicher Weise können wir eine Sprungtabelle erzeugen, um Funktionen anhand des Aufzählungswerts aufzurufen.

Dies erfordert, dass alle Funktionen dieselbe Signatur haben. Wenn sie keine Argumente akzeptieren und ein int zurückgeben, würden wir dies in einen Header mit der enum-Definition einfügen:

/* 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[];

Alle folgenden Elemente können sich in verschiedenen Übersetzungseinheiten befinden, vorausgesetzt, der obige Teil ist als Kopf enthalten:

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

Ein Beispiel für diese Technik, die in echtem Code verwendet wird, ist die GPU-Befehlsverteilung in Chromium .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow