0

実装と短いテスト/デモをここに投稿しました: http://ideone.com/CNJDi

私は、これまで見てきた他のものとは大きく異なる C++ Delegates へのアプローチを思いつきました。私が特に解決したい問題は、特定のインスタンスが a) メンバー fn ptr+オブジェクト ptr または b) 非メンバー fn ptr のいずれかである、統一された関数ポインター ソリューションを持つことです。デリゲートを初期化して他のコードに渡すと、デリゲートは、それがどのタイプのオブジェクトから来たのかさえ知らなくても、動的に呼び出すことができます (通常、メンバー関数ポインターは、それが来たクラスを静的に参照することによってのみ呼び出すことができます. Boo !)。理論上は容易に可能ですが、C++ ではこのようなことを実行するのは非常に困難です。だから私はそれを機能させるために言語をひねりました。

私の実装の主な欠点は、メンバー関数ポインターのキャスト/呼び出しが無効なため、技術的に「標準に準拠」していないことです (少なくとも、標準がここで私を保護していないことは 95% 確信しています...)。しかし、私テストしたコンパイラ (Visual Studio 2012 を含む) では動作します。また、メンバー関数がコンパイラによって簡単な方法で実装されている場合、それが「機能する傾向がある」ことは明らかだと思います。

私は他のいくつかの実装を見ましたが、私にはそれらが非常に複雑で使いにくいように見えました。メンバー関数を呼び出すスタブ関数を生成するためにビルド ツールに依存するものさえありましたが、私のものはマクロとテンプレートのみに依存していました。適切なデリゲートがないことは C++ の主な欠点だと思いますが、「この回避策が嫌いではない」ことがわかりました。実際に使いたいのか、それとも思いついたから気に入っただけなのかを判断する必要があります。

使用方法は次のとおりです。

A.デリゲートタイプを宣言する

typedef DELEGATE(float, ARGS(int, int)) Delegate1;

このマクロは、静的およびメンバー関数のポインター型を自動的に宣言するため、署名を 2 回入力する必要はありません。に展開しDelegate<float (*)(int, int), float (Null::*)(int, int)>ます。コンパイラは、後で呼び出しを実行するためにデリゲート インスタンスがどのように初期化されたかに応じて、これらのいずれかを使用します。また、コンパイラはそれを使用して、コーダーの呼び出しによって提供された引数を静的に検証します。ARGS マクロは、戻り値の型から分離するための純粋な構文糖衣です: DELEGATE(float, int, int) は同じです。

B. 初期化:

Delegate1 d = Delegate1(test1); // static function
Delegate1 e = Delegate1((Delegate1::MemberType)&TestClass::test2, &obj); // member

これらの静的関数とメンバー関数は、同じ型として格納されるようになりました! obj は TestClass インスタンスへの有効なポインターである必要があり、TestClass::test2 は float を返し、上記のように (int, int) を引数として取得する必要があります。これは主な使用上の落とし穴です。コンパイラはここで行われた間違いを見つけることができません。

C. Invoke: INVOKE(d, ARGS(5, 6)) (この例では float を返します) 見た目とは裏腹に、この引数リストは実際には C++ 関数呼び出しと同じくらい型安全です! 上記で提供された前述の float (*)(int, int) シグネチャを使用して引数を検証します! 任意の数の引数をサポートできますが、署名と一致する必要があります。追加する引数が多すぎたり少なすぎたり、コンパイラから間違った引数の型を使用したりすると、わかりやすいコンパイラ エラーが発生します。繰り返しますが、ARGS はシンタックス シュガーでINVOKE(d, 5, 6)あり、同じです。

しかし、承認しないコンパイラで INVOKE を使用すると、おそらくプログラムがクラッシュします:(

いくつか質問があります:

  1. 私の投稿した実装サンプルが正しく動作しない優れたコンパイラを誰か見つけることができますか?
  2. これが完全に機能するか、コンパイラ/プログラムが最初の使用時に反転することを願っています. しかし、しばらく動作しているように見えて、その後ランダムにクラッシュする可能性はありますか?
  3. あなたの意見では、これは使いやすい/きれいに見えますか? それとも、他の実装の方が簡単だと思いますか? どれ?どうにかして私の構文/使いやすさを改善する良い方法を考えてもらえますか?
  4. あなたはそれを使用しますか?それとも、弾丸を噛んで、より安全だがより複雑な代替ソリューションを使用する必要がありますか?
4

2 に答える 2

2

C++11 / Boost で利用可能な std::bind を使用しないのはなぜですか? メンバー関数、関数ポインター、ラムダ関数、ファンクター、およびその他の呼び出し可能なオブジェクトを使用できます。個人的には、プリプロセッサとマクロは何としてでも避けたいと思っています。デバッグが大混乱になる可能性があります。Scott Meyers (Effective C++、More Effects C++、Effective STL の著者) も、プリプロセッサの使用を推奨していません。

バインドのリファレンス: http://en.cppreference.com/w/cpp/utility/functional/bind

std::function を使用してさまざまな型をカプセル化することも検討しますが、 bind はほとんどすべてをきれいに処理できます。http://en.cppreference.com/w/cpp/utility/functional/function

于 2012-09-13T08:13:55.330 に答える
0

投稿した実装サンプルが正しく機能しない優れたコンパイラを誰かが見つけることができますか?

私の意見では、コードがそもそも標準に準拠していない場合、これは関係ありません。言語の規則を奇妙な方法で見なければならない場合、コードはおそらく標準に準拠していません。

コンパイラの実装の詳細は変更される可能性がありますが、結果のバイナリの観察された動作は変更できないため、標準に準拠したコードは重要です。標準準拠の問題であるバグは、非標準準拠のコードが機能しない問題であるバグよりも修正される可能性が高くなります。

これが完全に機能するか、コンパイラ/プログラムが最初の使用で反転することを願っています。しかし、しばらくは機能しているように見え、その後ランダムにクラッシュする可能性はありますか?

コードが未定義の動作を呼び出す場合は、それを正常に機能させ、最悪の場合に壊滅的に失敗する可能性が標準で許可されています。

あなたの意見では、これは使いやすい/きれいに見えますか?それとも、他の実装がもっと簡単だと思いますか?どれ?どういうわけか私の構文/ユーザビリティを改善するための良い方法を考えられますか?

正直なところ、それは混乱だと思います。デリゲートを使用するには、3つの異なるマクロ、明らかにオプションではない別個のtypedef、およびCスタイルのキャストを使用する必要があります。率直に言って、使用して見るのは苦痛です。

使ったことはありますか?それとも、弾丸を噛んで、より安全でより複雑な代替ソリューションを使用する必要がありますか?

繰り返しになりますが、正直なところ、私はstd::function友達と一緒に使用します(またはboost::function、C ++ 11コンパイラが利用できない場合)。あなたがstd::functionより複雑だと思ったとしても、それは確かによりクリーンで、標準的で、はるかに能力があります。

于 2012-09-13T08:26:02.943 に答える