プリプロセッサ基礎文法最速マスター

最速だけに催促されたので書きますね!

1.基礎

印字命令を見てみましょう。
lesson1-1.cpp

123
abc AAAAAAAAAAAAAAAAA!

と書いたファイルを実行すると、

123
abc AAAAAAAAAAAAAAAAA!

このようになります。見てのとおり書いたまんま印字されるので、特に印字するための命令とかはないです。これだけだと「おいプログラミング言語ちゃうんかボケが!」と罵られること請け合いなので、印字以外の命令を見ましょう。
lesson1_2.cpp

#define FOO 1
#define BAR A
FOO BAR FOO FOO BAR

結果は、

1 A 1 1 A

と印字されます(改行は適宜省略しています)。
「#define HOGE PIYO」と書くと、「以後に登場するHOGEをPIYOに置き換えますよ」という命令です。記号以外の文字が置き換え後として使える文字です。なお、この講座では一部を除いて、慣習として全て英大文字で書いています。次は更に複雑な命令です。
lesson1_3.cpp

#define ABC 1
#define DOUBLE(x) x x
DOUBLE(ABC) DOUBLE(POYO!)

これはこのような結果になります:

1 1 POYO! POYO!

「#define HOGE(x) PIYO x」は、「以後に登場する「HOGE(なんとか)」を、「PIYO なんとか」に置き換えますよ」という命令です。「なんとか」を引数と呼びます。これまでは置き換え命令ですが、逆に置き換えないようにする命令もあります。
lesson1_4.cpp

#define FOO 1
FOO
#undef FOO
FOO
1
FOO

「#undef HOGE」は、「以後に登場する HOGE は、もう置き換えないよ」という命令です。これは引数を取る置き換え命令に対しても有効です。

2.文字列

lesson2.cpp

#define STR(x) #x
STR(瀬戸内海)

これは、

"瀬戸内海"

と印字されます。だから何ですか?

3.制御文

lesson3_1.cpp

#include "lesson1_2.cpp"
#if FOO
#define ABC A
#else
#define ABC B
#endif
ABC
1 A 1 1 A
A

「#include "ファイル名"」は、ファイルをサブルーチンとして呼び出す機能です。「#if HOGE」は、「HOGEが 0 以外だったら、直後の行から #else の直前の行までを、そうでなければ #else の直後の行から #endif の直前の行までを実行します。「0 以外」とか「0」って何ですか?それは C や C++ での式の話です。なんとこの言語は別の言語の式を解釈できます。賢い! #if には、それらの言語におけるブール式として解釈な式を書けます。このとき、それらの言語での式として解釈なトークンは、それらの言語における式の値に変換され、そうでないトークンは全て、それらの言語での 0 として解釈されます。 #else は省略できます。省略した場合は、 HOGE が 0 以外だったら、直後の行から #endif の直前の行までを実行し、そうでなければ何も起こりません。 #if の中で書ける式については、それらの言語の解説を参考にしてください。 #if には、更にこんな機能があります:
lesson3_2.cpp

#define X
#if defined X
実行されません
#endif
実行されません

#if の中では、「defined なんとか」という形で、「なんとか が #define 命令で定義されていれば真、そうでなければ偽(真・偽いずれの場合も、C や C++ における意味として)」という特殊な式を書くことができます。これを使えば、ある名前が #define で定義されているかどうかを調べられます。「#if defined X」には、構文糖があり、「#ifdef X」と書けます。「#if !defined X」と書くと、これは「!」がCやC++における、論理否定演算子(!x で、 x が 0 以外だったら真、 0 なら偽)となるので、 defined X の逆となります。これにも構文糖が存在して、「#ifndef X」と書きます。 #if 命令及び関連する命令は入れ子が可能であり、
lesson3_3.cpp

#if 0
〜
#else
#if 1
〜〜
#endif
〜〜〜
#endif

というようなプログラムも書けます。これを実行すると、こうなります:

〜〜
〜〜〜

#if は構文糖が多くて、まだあります。
lesson3_4.cpp

#if FOO == 1
ここ?
#else
#if FOO == 2
それともここ?
#else
#if FOO == 3
はたまたここかしら?
#endif
#endif
#endif

これは、

#if FOO == 1
ここ?
#elif FOO == 2
それともここ?
#elif FOO == 3
はたまたここかしら?
#endif

こんな形に書き直すことができます。行数が減りました。次は、プログラムが失敗したことを表す構文です。
lesson3_5.cpp

#define FOO 0
#if !FOO
#error
#endif
これは印字されません

何も表示されません。FOO が(C や C++ で) 偽なら、 #error 命令によってプログラムは直ちに終了します。 #error の後に続けてメッセージを書くことができます。書いた場合は、終了時にそのメッセージが表示されます。
なお、繰り返しは構文としては存在しません。頑張って繰り返してください。

4.トークン連結

lesson4_1.cpp

#define CAT(a) a ## mimi
CAT(neko)
#define CAT2(a, b) a ## b
CAT(inu, mimi)
nekomimi
inumimi

#define 命令(引数付きも含む)の中では、「HOGE ## PIYO」と書くことで、HOGEPIYO というトークンにすることができます。 ## で連結した結果が、置き換え対象となるトークンだった場合は、置き換えられます。ちなみに引数のある #define 命令は、上のように書くことで2つ以上の引数を扱うこともできます。では、次の例を見てみましょう。
lesson4_2.cpp

#define CAT(a) a ## mimi
#define nekomimi inumimi
CAT(neko)
inumimi

「CAT(neko)」は「nekomimi」と置き換えられますが、既に「nekomimi」は「inumimi」と置き換える命令がありますので、「inumimi」になるわけです。このトークン連結機能と #define を組み合わせれば、四則演算にループ、配列を自分で作れますので、頑張って作ってみてください。あといくつか命令がありますが(プリプロセスで)特に有用な使い方を思いつかないので割愛しますね!

追記:
「そんなの知ってるぜベイベー!」っていう人の為に、このようなお話もあります。