- ベストアンサー
関数ポインタを返す関数の型をtypedefする方法
- C言語において、関数ポインタを返す関数の型をtypedefで定義する方法について質問します。
- 具体的には、intを引数に取り、その関数と同じ型の関数へのポインタを返す関数をtypedefしたいです。
- 純粋なCで解決する方法を教えていただけると助かります。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。 More Exceptional C++の213~214ページにそっくりそのままの用途の内容がありますね。 まず typedef FN (*FN)(int); と言ったこと自体は確かに不可能です。 ということで、これに似た事がやりたい場合はどうするか という方向性の話ですが 問題なのは typedef void* (*FN)(int); この形式では void*は関数ポインタ以外のポインタ(オブジェクトのポインタ)はどんなものでも保持できる十分な大きさが保証されているが、関数ポインタに対してはその限りではなく sizeof(void*) < sizeof(FN) となるプラットフォームも存在するし、別のコンパイラにしたり、バージョンアップするだけで、動作しなくなる可能性がある とのことです。 そう言う点だけであれば関数ポインタを使ってキャストすれば可能ではある とは書かれています。 ↓以下若干変えつつ引用、その1 ///////////////// typedef void (*VoidFP)(); typedef VoidFP (*FP)(); VoidFP f(){ return (VoidFP)f; } //VoidFPへのキャスト。 FP p = (FP)f; //VoidFPからのキャスト。 p(); このコードは技術的に正しい。 しかし、型システムを故意に破っている点で危険であり、f()の全ユーザにキャストの使用を強制する点で望ましくない。 ///////////////// とのことです。 そしてC++のコードになりますが 「正しく、かつ移植性のある方法」と銘打たれている方法は 若干変えつつ引用、その2 ///////////////// class FP_; typedef FP_ (*FP)(); class FP_ { FP p_; public: FP_( FP p ) : p_(p){} operator FP(){ return p_; } }; これで、FPオブジェクトの宣言、定義、および自然な呼び出しが可能になる。 FP_ f(){ return f; } int main(){ FP p = f(); //自然な呼び出しのシンタックス p(); } ///////////////// とのことですね。
その他の回答 (3)
- jacta
- ベストアンサー率26% (845/3158)
すでに指摘されているように、void*と関数へのポインタではサイズが異なる可能性があります。 そこで、こんな感じでどうでしょうか? #include <stdio.h> typedef void (* (*FN)(int) )(struct { int fake; }); #define call_FN(fn, arg) (FN)(((FN)(fn))(arg)) void (* fn1(int arg) )(struct { int fake; }) { printf("%d\n", arg); } void (* fn2(int arg) )(struct { int fake; }) { printf("%d\n", arg); return fn1; } int main(void) { FN fn = fn2; fn = call_FN(fn, 123); fn = call_FN(fn, 456); return 0; } 警告は出るかもしれませんが、ほぼ期待したものに近いことができると思います。 仮引数として無名構造体を受け取るようにすることで、call_FNマクロを介さずに呼び出そうとすると、確実にエラーを発生させられるように工夫しています。
お礼
返事が遅くなりまして申し訳ありません。 マクロを使ってキャストを隠蔽するというアイデアはあったのですが、マクロを介さなければエラーを出す、というアイデアとその手法は斬新ですね。 ただ、通常の関数呼び出しとはちょっと見え方が異なる、という点は残念ですね。まあ、呼び出し方が意味は分かるし、このくらいは非常に些細な点ですから、十分受け入れられる範囲だと思います。 おもしろいアイデアをありがとうございます。
- nda23
- ベストアンサー率54% (777/1415)
不可能です。 (1)typedefの前にFNが未定義 FNが不明だからエラー (2)typedefの前にFNが定義済み 二重定義なのでエラー
お礼
返事が遅くなって申し訳ありません。 ご丁寧にエラーの説明までしていただいてありがとうございます。 エラーになることは理解していますが、やりたいことを説明する方法として > typedef FN (*FN)(int); という書き方を敢えてした、とご理解いただければと思います。
- Tacosan
- ベストアンサー率23% (3656/15482)
「純粋な C」ではキャストが必須. C++ なら関数オブジェクトを返せばいいので余裕.
お礼
回答が遅くなって申し訳ありません。 「純粋なC」と書きましたが、書き間違いでC++はOKです。boostのような複雑なライブラリは無しでやりたかった、というのが本当の意図でした。 で、「関数オブジェクト」を返すという方法は考えつかなかったです。頭が固いのか、「余裕」でできるそうなんですが具体的な方法がまだ思いついていない状況です。 有益なヒントを与えていただいたことに感謝します。ありがとうございました。
お礼
回答が遅くなりまして申し訳ありません。 (More) Essential C++が一通り目を通しましたけど、More Exceptional C++は見たことがないですね。有益な情報をありがとうございます。 >void*は関数ポインタ以外のポインタ(オブジェクトのポインタ)はどんなものでも保持できる十分な大きさが保証されているが、関数ポインタに対してはその限りではなく あ、そうなんですか。奥が深いですね、C/C++は。 確かに純粋なハーバードアーキテクチャで、プログラム空間は広い(32bitアドレス)けど、データ空間は狭い(16bitアドレス)なんてCPUがあるかもしれませんね。すごくニッチな世界だと思いますが・・・。 その2の方法は、なるほどと思いました。関数ポインタを返そうとするのではなく、関数オブジェクトでラッピングして返すということですね。 # これがNo.1さんのいう「関数オブジェクトを使えば余裕」ということなんだろうか・・・ 非常に参考になりました。ありがとうございました。