関数ポインタを返す関数の型をtypedefする方法

このQ&Aのポイント
  • C言語において、関数ポインタを返す関数の型をtypedefで定義する方法について質問します。
  • 具体的には、intを引数に取り、その関数と同じ型の関数へのポインタを返す関数をtypedefしたいです。
  • 純粋なCで解決する方法を教えていただけると助かります。
回答を見る
  • ベストアンサー

関数ポインタを返す関数の型をtypedefする方法

C言語について質問します。 ある関数を定義するとします。 その関数は引数としてintを一つ取り、返値としてその関数と同じ型の関数へのポインタを返すようにしたいのですが、どのように書けばよいのでしょうか? そして、その関数の型をtypedefで定義したいです。 例えば、FNをtypedefしたいその関数の型だとすると、 typedef FN (*FN)(int); のようなFNを定義したいのですが、上のように書いても当然コンパイラ(VC9)に怒られます。 最悪、 typedef void* (*FN)(int); とvoidポインタを返すように定義しておいて、そのポインタを返値として受け取った側でFNにキャストし直す方法で対処できなくもないですが、ちょっと強引過ぎる気がします。 何かいい方法はあるのでしょうか? boost::functionあたりを使えばできそうな、そうでもないような気がしますが、できれば純粋なCでの解決法を望みます。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

こんばんは。 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(); } ///////////////// とのことですね。

haniriito
質問者

お礼

回答が遅くなりまして申し訳ありません。 (More) Essential C++が一通り目を通しましたけど、More Exceptional C++は見たことがないですね。有益な情報をありがとうございます。 >void*は関数ポインタ以外のポインタ(オブジェクトのポインタ)はどんなものでも保持できる十分な大きさが保証されているが、関数ポインタに対してはその限りではなく あ、そうなんですか。奥が深いですね、C/C++は。 確かに純粋なハーバードアーキテクチャで、プログラム空間は広い(32bitアドレス)けど、データ空間は狭い(16bitアドレス)なんてCPUがあるかもしれませんね。すごくニッチな世界だと思いますが・・・。 その2の方法は、なるほどと思いました。関数ポインタを返そうとするのではなく、関数オブジェクトでラッピングして返すということですね。 # これがNo.1さんのいう「関数オブジェクトを使えば余裕」ということなんだろうか・・・ 非常に参考になりました。ありがとうございました。

その他の回答 (3)

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

すでに指摘されているように、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マクロを介さずに呼び出そうとすると、確実にエラーを発生させられるように工夫しています。

haniriito
質問者

お礼

返事が遅くなりまして申し訳ありません。 マクロを使ってキャストを隠蔽するというアイデアはあったのですが、マクロを介さなければエラーを出す、というアイデアとその手法は斬新ですね。 ただ、通常の関数呼び出しとはちょっと見え方が異なる、という点は残念ですね。まあ、呼び出し方が意味は分かるし、このくらいは非常に些細な点ですから、十分受け入れられる範囲だと思います。 おもしろいアイデアをありがとうございます。

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.2

不可能です。 (1)typedefの前にFNが未定義  FNが不明だからエラー (2)typedefの前にFNが定義済み  二重定義なのでエラー

haniriito
質問者

お礼

返事が遅くなって申し訳ありません。 ご丁寧にエラーの説明までしていただいてありがとうございます。 エラーになることは理解していますが、やりたいことを説明する方法として > typedef FN (*FN)(int); という書き方を敢えてした、とご理解いただければと思います。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

「純粋な C」ではキャストが必須. C++ なら関数オブジェクトを返せばいいので余裕.

haniriito
質問者

お礼

回答が遅くなって申し訳ありません。 「純粋なC」と書きましたが、書き間違いでC++はOKです。boostのような複雑なライブラリは無しでやりたかった、というのが本当の意図でした。 で、「関数オブジェクト」を返すという方法は考えつかなかったです。頭が固いのか、「余裕」でできるそうなんですが具体的な方法がまだ思いついていない状況です。 有益なヒントを与えていただいたことに感謝します。ありがとうございました。

関連するQ&A

  • 関数ポインタの型をtypedefしたとき

    C言語において、関数ポインタの型をtypedefで作ると、 typedef int (*MyFunc)(int*,int*); と宣言でき、関数ポインタの変数は、 int FuncA(int* a, int* b) { ~ } void main_loop() { MyFunc pf = FuncA; ~ (*pf)(pa,pb); } というように使うと思います。 ここで疑問なのですが、この実際に呼び出される関数、FuncAの定義に、typedef(ここではMyFunc)を使えないものでしょうか? 同じことを2回やっているようで、無駄に思えてしまいます。

  • 関数ポインタについて

    関数ポインタを使用する際に、 指定する先の関数でデフォルト引数が指定されている場合、 関数ポインタを用いて、その引数を省略して呼び出すことはできるのでしょうか? 以下例とします。 void function(int a = 10){}; typedef void (*test)(int a); test a = &function; a(); //このように呼び出したい

  • C++のtypedefについて

    C++のtypedefについて C++(Borland C++ Builder V5)で以下のようなソースがありました。 『typedef int (func)(Xclass* pXcls);』 これって関数ポインタの定義でしょうか? 関数ポインタの定義の場合は以下のようになるのではないのでしょうか? 『typedef int (*func)(Xclass* pXcls);』 ご教授お願いいたします。

  • C言語のvoid型ポインタを使いたいのですが…

    C言語のvoid型ポインタを使いたいのですが… 関数の引数として、void型ポインタを使おうと思ったのですが、内部でどのように処理すればいいのかわかりません キャストすれば問題なく使えるとのことですが、どの型でキャストするのかをどのように判断するのかがわかりません 具体的には、画像処理で画像の構造体をいくつか作ったのですが、それぞれの構造体ごとに関数を書くと関数が多くなるので、void型ポインタでまとめてつくろうとしています どのように型の判断を行えばいいのかを教えてください

  • Voidポインタで受け取った変数の型を調べる方法

    あるライブラリの関数を利用しようとしたのですが、その関数の引数にVoidポインタがあり、どういう型のデータを格納したのかわかりません。このポインタのアドレスに格納されたデータの型を調べる関数なり方法なりというのはあるのでしょうか。 Voidポインタというのは型を問わずに受け取れるというのは利点ですが、受け取ったものがあらかじめなんだかわからない場合はどうするのでしょうか。よろしくお願いします。

  • 不透明なデータのポインタ宣言

    ライブラリ内で使用し、ライブラリの外には、 中身を公開したくない構造体があります。 ライブラリ関数には必ず、ポインタとしてやりとりされます。 これを外部向けに不透明なデータのポインタとして宣言したいのですが・・ typedef const void *PRIVATE_DATA ; このように宣言してみたのですが、 これでは、否応にもvoidポインタとなってしまい、 char *等と暗黙のキャストが行われてしまいます。 たとえば・・ int lib_func( PRIVATE_DATA priv ) ; こんなライブラリ関数に対して、 lib_func( "HELLO" ) ; こんなおかしな呼び出し方が書けてしまいます。 不透明で、他の型へ暗黙のキャストが行われないポインタの宣言の仕方って、 ありませんでしょうか。。? ちなみに、コンパイラは gcc version 3.4.4 です。

  • 関数の引数をvoid*でキャストする

    最近見かけたCのプログラムで、関数の引数の型は void* なのですが、その関数を使うときに 引数をvoid*でキャストしていました。 例えば、 func ( (void*) p ); こういうことです。 私の知っている知識では、 void* と 任意の型のポインタは キャストなしに相互に代入可能です。 関数の引数でも、キャストは要らないものだと思っていました。 そうすると、引数を void* でキャストするのは無意味だと思うのですが、・・・ 違うのでしょうか。処理系によるとか。 逆に、関数の引数の型がchar*などで、渡すものが void* のときはどうなのでしょうか。 下のプログラムは、関数byte_orderの引数の型はvoid*ですが、int型へのポインタ( &a )を設定しています。私の環境では、コンパイルエラーも警告もないし、動作も正常です。 #include <stdio.h> #include <string.h> void byte_order(void *vp) { char char_array[4]; strncpy(char_array, vp, 4); printf("出力します:%x %x %x %x\n", char_array[0], char_array[1], char_array[2], char_array[3]); } int main(void) { int a = 0x12345678; byte_order(&a); return 0; } このプログラムは単なる一例であって、質問はバイトオーダに関するものではありません。 また、C言語の質問であって、C++ではありません。

  • ポインタのポインタが引数にある関数の使い方。

    ポインタのポインタが引数にある関数の使い方。 現在、このポインタのポインタが引数にある関数の動きがわからず困っています。 int test(int ** head) { int * pTail = (int *)*head; pTail = pTail + 1; } もし、この関数を呼び出して使用した場合どのような動きをするのでしょうか? int * comm_msg; これをグローバルポインタ変数として宣言させて、 test((void **)&comm_msg); このように呼び出したとした場合とさせていただきます。

  • C言語での関数の引数の受け渡しについて

    C言語での関数の引数の受け渡しについて教えてもらいたいのです。 char *p=Goo;  というポインタpがmain関数で定義され、このポインタpをある関数 void func(・・・) に渡すことは出来ますか? つまりポインタを実引数として扱うことはできるのかという事ですが・・・ int p=10; とかだったら、 void func(int test) の関数には、main関数で func(p) で仮引数testにわたせると思うんですが・・・ もし出来るようでしたら、関数の渡し方と定義の記述を教えてください。 どうか宜しくお願いします。

  • CおよびC++におけるtypedefの厳密な扱われ方

    お世話になっております。 主に3D系のゲームプログラマをしている者です。 今更こんな基本的な事を聞くのもお恥ずかしいのですが、出来るだけ正確な確認をしたいので、 皆様のお力を借りれたらと思います。 知りたい部分は、typedefで型再定義された物は、 元の型と全く同じ型として扱われると思って問題ないのか。 と言う事です。 typedefは、型の同義語を作る。 とあり、型名を簡略化する目的で使用されるのが一般的な使われ方ですが、型の同義語。 と言うのは、完全に、同じ型ではあるが名前だけが違う。と言う別名定義(defineの如く)として 見て良いのでしょうか? 例えば、 typedef int INT_A; void func (int param); /* 通常のintで仮引数を取る */ int main(void) {  INT_A num = 0; /* 再定義したINT_Aで変数を宣言し */  func(num); /* 通常intの仮引数にINT_Aで引数を渡す */  return 0; } 等とした場合、問題ないでしょうか。 特に問題ない事は幾つかの処理系で確認していますし、自身問題ないだろうと思っていますが、 厳密に言えば推測の域を出ないので困っています。 参考書やネット、経験からの勝手な推測では、defineならプリプロセッサにより、 コンパイル前にテキストレベルでの置き換えが行われるので同一。 typedefはコンパイル時解釈とあるので、同じ型と解釈して、 オブジェクトファイル等のバイナリレベルに落とす際に同一物にしてしまう。 等の変換を処理系で行っているのなら、同一なのかな。等と思っています。 (私は処理系の開発等はした事がありませんし、知識もソフ開レベル程度があるだけです) また、上記コードはC/C++どちらでも適用出来ますが、 C++はCと比べ、型の違いをより厳密に見ると聞きます。 ネットや参考書、経験則と言った類からの意見ですと質問した意味が ありませんので、 なるべくなら、X3010および、X3014(ANSIやISOも可)の規格から見て、上記の様な コードが全く問題ないのかどうか、ご教示頂けると幸いです。 (私は規格を持っていませんので・・・) また、上の例はサンプルであり、使い方等におけるコードの 可読性や意義に関しては、ここでは別問題とします。 以上、宜しくお願いします。

専門家に質問してみよう