Hatena::ブログ(Diary)

にっき(pseudo)

2010/02/03(水) むしろやしろ

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

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

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 を組み合わせれば、四則演算にループ、配列を自分で作れますので、頑張って作ってみてください。あといくつか命令がありますが(プリプロセスで)特に有用な使い方を思いつかないので割愛しますね!

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

hito_hpphito_hpp 2010/02/03 18:41 さすがプリプロセッサを汎用的なプログラミング言語だと豪語する男だ。

__ 2010/09/12 03:12 「#define HOGE(x) PIYO x」

が見当たらない(ToT)/~~~

DigitalGhostDigitalGhost 2010/09/14 14:26 えーっと、それは「直前に書かれているコードにHOGEとか出てきてないよ」ってことですか?
そうだとしたらその部分は、

「#define ○○ ××」と書くと、「以後に登場する○○を××に置き換えますよ」という命令です。

という風に読み替えていただければ伝わるかと…
あと、HOGEとかPIYOとかFUGAというのは「メタ構文変数」と言って、○○とかそういうのと似たようなものです。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証