で、関数をデータの様に扱ってなにがうれしいのかと思うかも知れません。 たとえば、スレッドとかを扱うようになると、そのスレッドで実行してほしい関数を指定したかったりします。 そんなときは関数への pointer で、実行してほしい関数を指定します。
別の例も紹介しておきましょう。 例えば、 先ほどの pretty print のプログラムを各データの種類に幾つもつくりましたね。 で、それを switch 文をつかって書いていました。
void pprint_SData(SData_tp data) { /* tag に応じて、各関数を呼び出す dispatch関数 */ switch(data->tag) { case tagInt: /* Int の場合 */ pprint_SDataInt(SData_tp data); break; case tagDouble: /* Double の場合 */ pprint_SDataDouble(SData_tp data); break; case tagSymbol: /* Symbol の場合 */ pprint_SDataSymbol(SData_tp data); break; case tagComposite: /* Composite の場合 */ pprint_SDataComposite(SData_tp data); break; default: error(1); } }でも、こんな同じような関数を書いている場合、関数自身をデータのように扱えると結構便利です。
まず、データの様に扱うためには型が必要です。先ほどの pretty print 達は、
void pprint_SDataXXXX(SData_tp data);という形をしてましたから、typedef の際は、
typedef void (*pprint_SData_func_tp)(SData_tp data);でOKです。これでpprint_SData_func_tp は関数を示すpointerになりました。
次に、関数へのpointer の配列をつくりましょう。
pprint_SData_func_tp pprint_SData_functable[] = { pprint_SDataInt, /* 関数名を書くと、その関数へのpointerを表してくれる */ pprint_SDataDouble, pprint_SDataSymbol, pprint_SDataComposite, };さて、ここまで準備しておくと、先ほどの dispatch 関数は、
void pprint_SData(SData_tp data) { /* tag に応じて、各関数を呼び出す dispatch関数 */ pprint_SData_func_tp func = pprint_SData_functable[data->tag]; /* 対応する関数へのpointer を get */ (*func)(data); /* あとは呼び出すだけ */ }と書けます。ちょっと、いい感じがしません?
2001.11.22/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp