function へのpointerを使う


今回の話の最後は、関数へのpointer。関数をデータの様に扱うことができます。 ちょっと込み入っているので、分からない人はスキップしてもらって構いません。

で、関数をデータの様に扱ってなにがうれしいのかと思うかも知れません。 たとえば、スレッドとかを扱うようになると、そのスレッドで実行してほしい関数を指定したかったりします。 そんなときは関数への 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