Entry

プログラミングメモ - typedef で関数プロトタイプに型付けする

2008年05月14日

唐突に Expat を精読しよう!とか思い立ったもんで,昨日あたりから少しずつ読んでいます。で,読んでいたときに見つけた以下のコード……。あ,言語は C です。

typedef enum XML_Error PTRCALL Processor(XML_Parser parser,
                                        const char *start,
                                        const char *end,
                                        const char **endPtr);

static Processor prologProcessor;
/* snip... */

static enum XML_Error PTRCALL
prologProcessor(XML_Parser parser,
               const char *s,
               const char *end,
               const char **nextPtr)
{
    const char *next = s;
    int tok = XmlPrologTok(encoding, s, end, &next);
    return doProlog(parser, encoding, s, end, tok, next,
                    nextPtr, (XML_Bool)!ps_finalBuffer);
}

注釈を入れておくと,prologProcessor() 内の doProlog() は enum XML_Error 型の値を返します。また、typedef の PTRCALL は空っぽなので,実質的には(プリプロセッサを抜け後では)次のコードと同じになります。ちょっと読みづらいので,解釈単位を括弧でくくりました。

typedef enum XML_Error (Processor)(XML_Parser parser,
                                  const char *start,
                                  const char *end,
                                  const char **endPtr);

で、ここでの typedef なんですけど,この使い方はすごいなぁ……。「enum ほげほげ」が戻り値の関数を,型として使っているように見えます。実際,以降の prologProcessor はまるで変数のように宣言されている……。こういう書き方,雰囲気としては分かるんですけど,文法的(仕様的)に裏付けが取れなかったので,ちょっと調べてみました。こんな風に書いてあった。

Function prototypes may be used to define function types as well:
(関数プロトタイプは関数の型を定義することにも使える)

typedef double (*d_binop) (double A, double B);
struct d_funct {
   d_binop f1;
   int (*f2)(double, double);
};

struct d_funct has two members, both of which hold pointers to functions taking two double arguments; the function types differ in their return type.
(構造体 d_funct には2つのメンバがあり,両者とも double の引数を2つ取る関数へのポインタを保持するが,戻り値の型が異なる。)

Rationale for International Standard. Programming Languages. C Revision 5.10

ほぉ……知らなんだ。

でもって,この書き方なんですけど,何が嬉しいのかというと……えーと……何が嬉しいんだろ。関数をわざわざ型付けして管理しておく必要があるんでしょうか。

と,ちょうどここら辺の話についての話題を見つけたので,引用しておきます(体裁は適宜修正)。

[Q] 下記のように、関数のプロトタイプ宣言をtypedefするのは何の意味があるのですか?

typedef void (*inquiry_cb)(int devNum); 

[A] C++の叙述関数を調べるとなんとなくわかってくるかもしれません

例えば、

hoge a,b;
sort(a,b,hoge型の並べ方の関数);

hoge型というclassがあって、そのソートの仕方、つまり、aとbとどっちが大きいか、小さいか、同じかの判断の仕方を、関数として渡すのが叙述関数(だったはず)です。例で言う「hoge型の並べ方の関数」がそれです。

関数は、関数ポインタとして関数に渡して、渡した関数に「使ってもらう」っていう使われ方もあるので、「使ってもらう」渡し方として、関数の型を決める際に、typedefするのも意味があると思います。

下記のように、関数のプロトタイプ宣言をtypedefするのは何の意味があるのですか?... - Yahoo!知恵袋

叙述関数(Predicate Functions)というのは,C++ で真偽値を返す関数オブジェクトのことなんですけれど,この説明ではちと不十分な気がします。というのも,この場合,返値が真偽値だけじゃないし,叙述関数のような限定的な目的のために使われるとは限らないからです。

Expat はストリーム型(ハンドラ型)の XML パーサですから,関数そのものをゴニョゴニョやらなくちゃいけない場合が多かったりします。ソースをまだちゃんと読んでないんですけど,多分,そういう需要からしてみると,関数プロトタイプを typedef した方が都合がいい場合ってのがあるのかもしれません。例えば,関数プロトタイプを typedef したら,こんなことができるはず(状態(status)に合わせて呼び先の関数を変える)。

typedef int Processor(int code);

static Processor funcError;
static Processor funcNormal;

int userFunc()
{
    int status;
    int code
    Processor myProcessor;

    /* 何かの処理 */
    
    myProcessor = (status == ERROR)
        ? funcError
        : funcNormal;

    return myProcessor(code);
}

JavaScript みたいだ!

ちょこっと読んだところ,Expat は関数周りにかなり気を使っているようで,Hack 満載だったりします。Hack ののっけから面白いもんを見せてもらいました。後が楽しみ。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN