C Language
Xマクロ
サーチ…
前書き
Xマクロは、コードの繰り返しを最小限に抑え、データ/コードの対応を維持するためのプリプロセッサベースの手法です。共通のデータセットに基づく複数の別個のマクロ拡張は、単一のマスターマクロを介して拡張グループ全体を表現することによってサポートされ、そのマクロの置換テキストは、各データに対して1つの内部マクロの一連の拡張からなる。内側のマクロは、伝統的にX()
という名前が付けられているため、テクニックの名前です。
備考
Xマクロスタイルのマスターマクロのユーザーは、内部のX()
マクロと、その範囲内でマスターマクロを展開する独自の定義を提供することが期待されます。したがって、マスターの内部マクロ参照は、ユーザーのX()
の定義に従って拡張されます。このようにして、ソースファイル内の反復可能な定型コードの量は、「自分自身を繰り返さない」(DRY)という考え方に支持されているように、 X()
置換テキストで1回しか表示されません。
さらに、 X()
を再定義し、さらに1回または複数回マスターマクロを展開することにより、Xマクロは対応するデータとコードの管理を容易にすることができます。マクロを展開するとデータが宣言されます(たとえば、配列要素または列挙型メンバとして)。他の拡張は対応するコードを生成する。
「Xマクロ」という名前は内部マクロの伝統的な名前に由来しますが、テクニックはその特定の名前に依存しません。その代わりに、任意の有効なマクロ名を使用できます。
批判には
- 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を返すならば、これを列挙型定義のヘッダーに入れます:
/* 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コマンドのディスパッチです。