第20章 トークンを結合せよ

 普通こんなものを講座で教えるものかと思いはしますが、第3部ではそんなことも平気で教えます。知っておくと思わぬところで使えることもあるので、知っておくに越したことはないでしょう。


 では、今回の要点です。


 では、いってみましょう。


 今回は、関数のプロトタイプと、その関数への関数ポインタの型定義を同時に行うマクロを作ってみたいと思います。

 言葉で説明するのも分かりにくいので、実際にさっさと作ってみましょう。

#define PT_FP(ret, name, fpname, params)  ret name params; \
                                          typedef ret (*fpname)params

 行の最後に \ がつけるのは、マクロ定義を複数行に書くときの約束でしたね。

 戻り値の型、関数名、ポインタの型名、そして引数をカッコ付きで書きます。実際に使ってみると、こんな感じになります。

PT_FP(int, Add, FP_Add, (int a, int b));
FP_Add fpAdd = Add;

 この PT_FP は次のように展開されます。

int Add (int a, int b); typedef int (*FP_Add)(int a, int b);

 引数リストをカッコ付きで渡しているので、展開されてもカッコがついています。それがちょうど関数のカッコになってくれるわけです。これで、引数が何個あっても1つのマクロを使うだけで大丈夫ですね。

 なお、\ を付けていても、マクロは1行に展開されることにも注意して下さい。(実際には typedef の前に空白が沢山書いてあるので typedef の前がもっと空くのですが、見づらくなってしまうので空けません。)


 しかし、関数名とポインタの型名には普通はほとんど同じ名前を付けるでしょう。上のも、ポインタの型名は関数名の頭に FP_ を付けただけです。これだと、わざわざ別のマクロ引数を使うのも2度手間な感じですね。

 そこで、Add を渡しただけで FP_ を勝手に付けてくれるようにできればいいですね。ということで、次のようにしてみるとどうなるでしょうか?

#define PT_FP(ret, name, params)  ret name params; \
                                  typedef ret (*FP_name)params

 では、PT_FP(int, Add, (int a, int b)); を展開してみましょう。

int Add (int a, int b); typedef int (*FP_name)(int a, int b);

 ...FP_name が FP_name のままですね。そういえば、これで大丈夫だったら

#define PRINT(r)   printf(r)

PRINT("Hello!");

としたとき、

p"Hello!"intf("Hello!");

になってしまいますね。

 ということで、こういうときはトークン結合子 ## というものを使います。例えば、次のようになります。

#define PT_FP(ret, name, params)  ret name params; \
                                  typedef ret (*FP_##name)params

 FP_ と name の間に ## が入ってますね。こうすると、マクロが展開されるときには FP_ と 引数 name がくっついて展開されます。つまり、

int Add (int a, int b); typedef int (*FP_Add)(int a, int b);

のように展開されるわけです。

 これでやっと望み通りの動作になってくれました。


 トークン結合子の機能をもうちょっと一般的に言うと、マクロ展開後に ## は消えてしまうとなります。例えば、

#define PRINTF(a)   a##printf

で、PRINTF(f) は先ず f##printf と展開され、その後に ## が消えて fprintf となります。また、

#define ABC a##b##c

で、ABC は a##b##c と展開され、そして ## が消えて abc となります。そして

#ifdef _DEBUG
#define ONLYDEBUG
#else
#define ONLYDEBUG  /##/
#endif

は、リリース時には ONLYDEBUG は /##/ と展開され、そして ## が消えて // 、つまりコメントになります。

 トークン結合子の実際使われている例には、Windowsx.h の HANDLE_MSG マクロ、Winnt.h の DECLARE_HANDLE マクロ、Richedit.h の _WPAD マクロなどがあります。

 トークン結合子はそうそう使う機会はありませんが、たまに「おっ! つこたらきれいになるやん」ということがあります。知っておいて損はないと思いますよ。


 では、今回の要点です。


 次回からはアルゴリズムのお話に入っていきたいと思います。ほぼ独学の自分がどこまで話せるかは分かりませんが、何とか形にしたいと思います。それでは。


第19章 組み込みマクロ | 第21章 call itself

Last update was done on 2000.9.20

この講座の著作権はロベールが保有しています