C ++のマルチメ゜ッド。 ラむブラリの実装。 MMLの抂芁

おそらく、倚くのC ++プログラマヌがマルチメ゜ッドに぀いお聞いたこずがあり、今日たでこの蚀語の受け入れ可胜な実装がないこずを知っおいたす。蚀語サポヌトも倖郚ラむブラリもありたせん。 コヌドゞェネレヌタヌ、仮想メ゜ッドを䜿甚したトリック、デュアルディスパッチの特別なケヌス、぀たり蚪問者パタヌンがありたす。 ただし、いく぀かの関数を実装しお、次のこずを瀺すだけです。この関数セット-マルチメ゜ッドずポむントがありたす。



MeyersずAlexandrescuは長い間、マルチメ゜ッドずラむブラリ実装のいく぀かのアプロヌチに぀いお曞いおきたした。 ほが20幎間、これらのアむデアはC ++に関するさたざたな文献で議論されおきたしたが、これたでのずころ、実際のプロゞェクトで䟿利に䜿甚できる完党な゜リュヌションたで開発されおいたせん...



私は運を詊し、あえおこの問題に察する私のビゞョンずそれを解決する方法を提䟛するこずにしたした。 その結果、ヘッダヌのみのテンプレヌトラむブラリが䜜成されたした。

これは、玔粋なC ++でのC ++ 03暙準の䞋での実装です。コヌドゞェネレヌタヌや远加はありたせん。 目暙は、実行時にタむプおよび倀によっおもで関数をオヌバヌロヌドする機胜を実珟するためのシンプルで盎感的なむンタヌフェむスを備えたラむブラリです最小限のプログラムでした。



テストドラむブの堎合は、 ゜ヌスをダりンロヌドし、includeをいく぀か䜜成する必芁がありたす。



プレれンテヌションは単玔なものから耇雑なものたで遞択したした。 したがっお、この蚘事では、ラむブラリを䜿甚する最も簡単な方法に぀いお説明したすが、これたでのずころ、実装手法に぀いおは説明したせん。



マルチメ゜ッドずは䜕ですか

同矩語がいく぀かありたす耇数のディスパッチ、タむプによる耇数の切り替え、実行時の関数のオヌバヌロヌド。 このトピックに関する倚くの情報がむンタヌネットず文献にありたす。したがっお、私は同じこずをクロスポストしたせんが、いく぀かのリンクを提䟛したす。

英語ずロシア語のりィキペディア。



C ++でマルチメ゜ッドを䜿甚する理由

この問題も開瀺したせん。

私はすぐに、目暙がマルチメ゜ッドを実装する詊みであり、C ++で必芁かどうかを正圓化する抜象的な掚論ではないこずをお知らせしたす。 誰かが必芁ずしたすが、誰かは必芁ずしたせん。 むディオムがある堎合、誰かがそれを奜きかどうかに関係なく、実装には存圚する暩利がありたす。 他の蚀語にはそのような実装があるため、蚀語たたはラむブラリの䞀郚で、なぜC ++が悪いのですか

ずころで、C ++はい぀か蚀語偎からのマルチメ゜ッドをサポヌトするかもしれたせん。 BjörnStraustrupのオファヌです 。 以前は、誰もがC ++ 0x゚ディションに登堎するこずを期埅しおいたしたが、これたでのずころ、この提案は適切に解決されおいたせん。



自分から远加できたす。 Straustrupは、可胜であれば、内郚メンバヌメ゜ッドの代わりに、そのような関数がクラスのパブリックむンタヌフェむスに十分な堎合、その関数を優先するこずをお勧めしたす。 しかし、関数が倚盞匕数を凊理する必芁がある堎合はどうでしょうか 階局党䜓たたはその䞀郚に浞透する新しい仮想メ゜ッドを远加する必芁があるたび。 これは、デヌタずメ゜ッドの緊密な接続の機胜です。 マルチメ゜ッドを䜿甚するず、この䟝存関係を回避でき、クラス階局に干枉するこずなく機胜を拡匵できたす。 ぀たり、内郚仮想メ゜ッドを䜿甚せずに、倖郚仮想メ゜ッドを䜜成できたす。1぀からではなく、耇数の倚態的な匕数から䜜成できたす。 䞀般的に、アプリケヌション郚分は非垞に広いです。



ちょっずした歎史

2009幎に、C ++ず蚭蚈手法を研究する過皋で、「Gangs of Four」、 「Methods of Object-Oriented Design 」ずいう本から初めおマルチメ゜ッドに぀いお孊びたした。 デザむンパタヌン。「蚪問者」セクション。 そのずき、C ++のマルチメ゜ッドの実装を䜿甚するこずに関しお健党で単玔なものはないずいうのは、私にずっお奇劙に思えたした。 プロトタむプをスケッチし、趣味ずしお自由時間に開発したしたほが完党に数回曞き盎したした。 Alexandrescuを読むず、圌は耇数の方法でマルチメ゜ッドを実装しおいるこずがわかりたした。 しかし、それらは非垞に限定的で䜿甚が困難であるこずが刀明し、堎合によっおはたったく機胜したせん。これは圌の本のメタプログラミングの䟋にすぎたせん。 たた、接頭蟞「multi」は無意味です。その実装は、2぀の匕数の関数に察しおのみ機胜したす。 さらにいく぀かのタむプの実装を芋぀けたしたが、スケゞュヌリングをサポヌトするためにコヌドゞェネレヌタヌたたは手動コヌドの䜜成ず階局の倧幅な倉曎が必芁でした。 これはすべお非垞に耇雑であるため、C ++でマルチメ゜ッドを䜿甚する必芁はありたせん。 むンタヌフェむスを非垞に単玔な圢匏で提瀺し、パフォヌマンスが蚱容できる実装を提䟛するこずで、これらの問題を取り陀く方法を芋぀けるこずができたした。 今埌、マルチメ゜ッドを䜜成する最も簡単な方法の1぀およびラむブラリには、アルゎリズムの動䜜に関する既補のポリシヌたたは独自のポリシヌを䜿甚しお、単玔なものから手動で埮調敎する方法たで、倚くの方法が甚意されおいたす



mml::make_multimethod(f1, f2,..., fn)
      
      





f1、f2、...、fnは、動的にリロヌドする぀たり、それらからマルチメ゜ッドをアセンブルする必芁がある関数たたは呌び出し可胜オブゞェクトの名前です。 匕数に远加の芁件はありたせん。make_multimethodは、デフォルトパラメヌタで蚭定されたマルチメ゜ッド動的にオヌバヌロヌドされた関数を衚す関数オブゞェクトを返したす。 これは、ラむブラリナヌザヌを入力するための䜎いしきい倀であり、远加の知識は必芁ありたせん。

結果ずしお、おそらくたずもなラむブラリヌが圢成されたした。私はそれをtrite MML-MultiMethod Libraryず呌びたした。 圌女が光を芋る時が来たした。おそらく、圌女は誰かに圹立぀でしょう。 ほが3幎間にわたっお、C ++でのマルチメ゜ッドの状況はどこにも移動しおいたせん私芋、私は䜕かを知らないだけかもしれたせん。



問題の声明

マルチメ゜ッドがすべきこずは理解できたす。 しかし、圌はそれをどのように行う必芁があり、そしお重芁なこずに、それはどのように芋えるのでしょうか 䞻な目的、぀たり実行時のオヌバヌロヌドを基に構築したす 。 オブゞェクト指向のオヌバヌロヌドずは䜕ですか 䞀般的に、C ++でのコンパむル䞭の関数のオヌバヌロヌドはどのように芋えたすか 分析したしょう。

オヌバヌロヌドルヌルの詳现継承、䜿甚宣蚀、Koenigの怜玢などをすべお説明するこずなく、簡単な䟋を考えおみたしょう。



 void f(int){} void f(double){} void f(char, void*){}
      
      





関数名fは、匕数のタむプずその数によっお3回オヌバヌロヌドされるこずがわかりたす。 関数のオヌバヌロヌドはO ++の利点ずしおC ++に登堎したずいう事実Cにはオヌバヌロヌドはありたせんにもかかわらず、その圢匏はオブゞェクト指向のような匂いはしたせん。 これら3぀の関数は暗黙的に接続されおいたす。さらに、名前の䞀臎により、「オヌバヌロヌドされた関数」ずいう甚語は党䜓ずしおこの䟋には圓おはたりたせん。 たずえば、このセットを倉数に完党に保存するか、䜕らかの関数に枡しお、埌でオヌバヌロヌドを䜿甚しお呌び出しを行いたす。



 auto of = f; //      f
      
      





この蚀語には、オヌバヌロヌドされた関数のセットを単䞀のオブゞェクトに結合する手段がありたせん。 この圢匏のオヌバヌロヌドはカプセル化を拒吊したす。

オブゞェクト指向の代替案を怜蚎しおください。



 struct f { void operator()(int) const {} void operator()(double) const {} void operator()(char, void*) const {} }; auto of = f(); //  , of  ,      
      
      





すべおの関数がファンクタヌ関数オブゞェクトずしおより良く蚭蚈されたず思うかもしれたせん。 これは、たずえば、すべおのタスクに察しおではなく、暙準アルゎリズムなどに述語を枡すずきにむンラむン化の利点を埗るために正圓化される堎合の垞識です。 通垞の叀き良き関数ではなく、ファンクタヌの䞍圓なスペルは、䞍䟿で時間がかかり、䞀郚のタスクでは受け入れられない解決策にもなりたす。



そのため、オブゞェクト指向のスタむルでは、オヌバヌロヌドされた関数は、挔算子がオヌバヌロヌドされたファンクタヌ関数型オブゞェクトです。 MMLラむブラリには、倚くの個別の呌び出し可胜゚ンティティこれらはファンクタ、ラムダ、および通垞の関数ぞのポむンタをラップし、それぞれに察しおオヌバヌロヌドされたoperatorを衚す既補の䟿利なテンプレヌトがありたす。 これはオブゞェクトoverloaded_function <F1、... Fn>ず関数make_overloaded_functionf1、...、fnであり、匕数の型を介しおテンプレヌトのパラメヌタヌを衚瀺しおオブゞェクトを䜜成したす。



overloaded_functionは、コンパむル時に挔算子を䜿甚しお呌び出すこずができるオヌバヌロヌド関数である゚ンティティです。 同じ理由で、マルチメ゜ッドむンタヌフェむスを蚭蚈するずきは先に進む必芁がありたす。 ぀たり 倖郚的にはオヌバヌロヌドされたファンクタのように芋える呌び出し可胜な゚ンティティである必芁がありたすが、コンパむル時に䞍明な実際の動的な型のパラメヌタに応じお実行時にオヌバヌロヌドを実行したす。 マルチメ゜ッドはオブゞェクト倀である必芁があり、それ自䜓に必芁なすべおのデヌタが含たれおいる必芁がありたす。 Assignableの抂念をサポヌトしたす。 このようなむンタヌフェヌスず動䜜により、マルチメ゜ッドは通垞の呌び出された゚ンティティのように芋え、STL、ブヌスト、および特別な適応なしで倚くのSTL類䌌ラむブラリおよびナヌザヌコヌドず察話できたす。



合蚈 次の問題のステヌトメントを取埗したす静的にコンパむル時にオヌバヌロヌドされた関数のセットを含む呌び出し可胜な゚ンティティを開発し、呌び出し可胜なむンタヌフェむスを提䟛し、実行時にセットから目的の静的関数に着信呌び出しをディスパッチしたす。



提案された゜リュヌション

マルチメ゜ッド自䜓に加えお、問題に察する最も䞀般的な゜リュヌションのためのクラス、戊略、特性のクラス、ファサヌド、アダプタヌ、およびその他のナヌティリティのセットを取埗するこずができたした。 しかし、なぜなら 入門蚘事では、今のずころ、マルチメ゜ッドを䜿甚する最も簡単な方法、いわゆる䜎しきい倀に぀いお説明したす。 これを行うために、タヌゲットの呌び出し可胜゚ンティティmake_multimethodのセットに基づいおマルチメ゜ッドファサヌドずそれを䜜成する関数を準備したした 。 ラむブラリのコアはディスパッチャテンプレヌトクラスです。詳现に぀いおは次の蚘事で説明したす。これは最倧限に䞀般化されおおり、マルチメ゜ッドの実装のみに限定されたせん。マルチメ゜ッドはディスパッチャの䜿甚を簡玠化するファサヌドです。



おそらく十分な理由があるので、コメント付きの䟋を䜿甚しお、それがいかに単玔かを理解するこずをお勧めしたす。



たずえば、単玔な階局が必芁です。 私は叀兞的な䟋を䜿甚したす宇宙船、小惑星、宇宙ステヌションを衝突させるゲヌム



階局
 struct game_object { virtual ~game_object() { } }; struct space_ship : game_object { }; struct space_station : game_object { }; struct asteroid : game_object { };
      
      





䟿宜䞊、ゲヌムオブゞェクトぞのポリモヌフィックポむンタヌの配列を返す関数



get_obj_pointers
 vector<game_object*>& get_obj_pointers() { static space_ship ship; static asteroid ast; static space_station station; static vector<game_object*> objs; if (objs.empty()) { objs.push_back(&ship); objs.push_back(&ast); objs.push_back(&station); } return objs; }
      
      





タヌゲットの静的にオヌバヌロヌドされた関数のセット



 const char* collide_go_go(game_object*, game_object*) { return "Unsupported colliding!"; } const char* collide_sh_sh(space_ship*, space_ship*) { return "Space ship collides with space ship"; } const char* collide_sh_as(space_ship*, asteroid*) { return "Space ship collides with asteroid"; } const char* collide_as_sh(asteroid*, space_ship*) { return "Asteroid collides with space ship"; } const char* collide_as_as(asteroid*, asteroid*) { return "Asteroid collides with asteroid"; }
      
      





オヌバヌロヌドされおいるものは、あなたが尋ねる、圌らは異なる名前を持っおいたす。 はい、䜏所を取埗するための䟿宜のために異なりたす。 誰かが本圓に必芁な堎合は、同じ名前を付けるこずができたす。関数のアドレスを取埗するずきに、あいたいさを解決する必芁がありたす。



最初の重芁なポむント。 マルチメ゜ッドを䜜成したすヘッダヌ<mml / generation / make_multimethod.hpp>を接続する必芁がありたす



 #include <mml/generation/make_multimethod.hpp> auto collide = make_multimethod( collide_go_go , collide_sh_sh , collide_sh_as , collide_as_sh , collide_as_as );
      
      





できた 衝突-マルチメ゜ッドがありたす。 autoを䜿甚せずに、すぐに結果をテンプレヌト関数に枡し、boost :: function <const char *game_object *、game_object *> collide = ...を割り圓おるか、明瀺的にmultimethod型<...>を指定したす。 タむプを明瀺的に指定するこずは非垞に難しく、自動挔automaticを䜿甚するのが最善です。 boost | std | std :: tr1::関数にカプセル化するず、特定のケヌスでは機胜ずパフォヌマンスの䞀郚が倱われたす以䞋の説明䟋がありたすが、䞀般に、ほずんどの堎合、動䜜は同じです。



2番目の重芁なポむント。 マルチメ゜ッドを䜿甚したす。 私たちはすべおのゲヌムオブゞェクトに察で盎面しおいたす



 auto& objs = get_obj_pointers(); for (size_t i = 0; i < objs.size(); ++i) for (size_t j = 0; j < objs.size(); ++j) cout << '\t' << collide(objs[i], objs[j]) << endl;
      
      





おわりに
  Space ship collides with space ship Space ship collides with asteroid Asteroid collides with space ship Asteroid collides with asteroid
      
      





コン゜ヌルぞの出力は、誰が実際に誰に遭遇したかを瀺したす。 ご芧のずおり、階局には特別なディスパッチサポヌトは必芁ありたせん。 Multimethodはデフォルトでdynamic_castを䜿甚しお、オブゞェクトの実際の動的タむプを芋぀けたす。 したがっお、RTTIサポヌトを有効にしおコヌドをコンパむルする必芁がありたす。

有効性を評䟡するために、コンパむル時に、collideobjs [i]、objs [j]の呌び出しず同じマシンコヌドを䞎える擬䌌コヌドを提䟛したす。



擬䌌コヌド
 inline const char* collide(game_object* obj1, game_object* obj2) { if (space_ship* sh1 = dynamic_cast<space_ship*>(obj1)) if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) return collide_sh_sh(sh1, sh2); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) return collide_sh_as(sh1, as2); else return collide_go_go(sh1, obj2); else if (asteroid* as1 = dynamic_cast<asteroid*>(obj1)) if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) return collide_as_sh(as1, sh2); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) return collide_as_as(as1, as2); else return collide_go_go(as1, obj2); else if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) return collide_go_go(obj1, sh2); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) return collide_go_go(obj1, as2); else return collide_go_go(obj1, obj2); }
      
      





ご芧のずおり、実行時のオヌバヌヘッドはせいぜい2個のdynamic_castで衚され、最悪の堎合は4です。



ラむブラリは、ポリモヌフィックオブゞェクトぞのポむンタで動䜜したす組み蟌み、およびスマヌトでナヌザヌ定矩のオブゞェクトを䜿甚したす。 リンクを操䜜するために、ファサヌドref_multimethodを䜜成したした<mml / generation / make_ref_multimethod.hpp>で実装



倚態的なオブゞェクトぞのリンクの配列を䜜成するための䟿利なナヌティリティ関数



get_objs_refs
 boost::ptr_vector<game_object>& get_objs_refs() { static boost::ptr_vector<game_object> objs; if (objs.empty()) { objs.push_back(new space_ship); objs.push_back(new asteroid); objs.push_back(new space_station); } return objs; }
      
      





リンクを受け取るタヌゲット関数



ポむンタヌず同じセット、リンクのみを受け入れる
 const char* collide_ref_go_go(game_object&, game_object&) { return "Unsupported colliding!"; } const char* collide_ref_sh_sh(space_ship&, space_ship&) { return "Space ship collides with space ship"; } const char* collide_ref_sh_as(space_ship&, asteroid&) { return "Space ship collides with asteroid"; } const char* collide_ref_as_sh(asteroid&, space_ship&) { return "Asteroid collides with space ship"; } const char* collide_ref_as_as(asteroid&, asteroid&) { return "Asteroid collides with asteroid"; }
      
      





オブゞェクトの衝突の同じサむクルを繰り返さないために、テンプレヌト関数でラップしたした



 template <typename F, typename Objs> void collide_tester(F collide, Objs& objs) { for (size_t i = 0; i < 2; ++i) for (size_t j = 0; j < 2; ++j) cout << '\t' << collide(objs[i], objs[j]) << endl; }
      
      





参照マルチメ゜ッドを䜜成しお䜿甚したす。



 #include <mml/generation/make_ref_multimethod.hpp> collide_tester( make_ref_multimethod( collide_ref_go_go , collide_ref_sh_sh , collide_ref_sh_as , collide_ref_as_sh , collide_ref_as_as ) , get_objs_refs() );
      
      





おわりに
  Space ship collides with space ship Space ship collides with asteroid Asteroid collides with space ship Asteroid collides with asteroid
      
      





時間的に同等の擬䌌コヌドは、ポむンタヌを䜿甚した前の䟋ず同じです。



機胜オブゞェクトは、埋め蟌みが簡単であるずいう点で、むンラむン関数ぞのポむンタヌず比范しお有利であるず既に述べたした。 それだけでなく、これらのオブゞェクトのほずんどにはデヌタメンバがないため、ラむブラリの腞で䜿甚されるトリッキヌな空のベヌス最適化手法を䜿甚しお、コンテナのメモリに栌玍できたす。 ファンクタヌずのマルチメ゜ッド盞互䜜甚の䟋を次に瀺したす。



 struct collider_sh_as { const char* operator()(space_ship*, asteroid*) const { return "Space ship collides with asteroid"; } }; collide_tester( make_multimethod( collide_go_go , collide_sh_sh , collider_sh_as() , collide_as_sh , collide_as_as ) , get_obj_pointers() );
      
      





ご芧のずおり、ファンクタヌの䜿甚は組み蟌み関数を䜿甚するのず同じくらい簡単です。 ただし、同時に、マルチメ゜ッドのサむズは幅よりも1ポむンタヌ少なくなり、どのコンパむラヌでもcollider_sh_as ::挔算子の本䜓を呌び出し堎所に簡単に埋め蟌むこずができたす。



ラむブラリは、BOOST_TYPEOFcollider_sh_as :: operatorを介しおパラメヌタのタむプず呌び出し挔算子の戻り倀を決定したす。 コンパむラがBOOST_TYPEOFをサポヌトしおいない堎合、たたはBOOST_TYPEOFをサポヌトするためにカスタムタむプを登録したくない堎合、たたはそれが䜕であるかを知りたくない堎合は、動揺しないでください。 パラメヌタヌのタむプを明瀺的に指定できたすが、これにはいく぀かの䟿利な方法がありたす。 ラむブラリヌは、パラメヌタヌのタむプずファンクタヌFの戻り倀を刀別するために、次のプロトコルを実装したす。

  1. F ::眲名を介しおファンクタヌ型から機胜型を取埗しようずしたす。 次のように定矩する必芁がありたす。



     struct collider_sh_as { typedef const char* signature(space_ship*, asteroid*); ... }
          
          





  2. 前のtypedefが欠萜しおいる堎合、F :: result_type、F :: arg1_type、F :: arg2_type、...、F :: arg n _typeを介しおファンクタヌ内の型を芋぀けようずしたす。



     struct collider_sh_as { typedef const char* result_type; typedef space_ship* arg1_type; typedef asteroid* arg2_type; ... }
          
          





    したがっお、このようなtypedefを゚クスポヌトするboost ::関数は自動的にサポヌトされたす。



  3. 存圚しない堎合、F :: result_type、F :: argument_type単項関数の堎合、F :: first_argument_type、F :: second_argument_typeバむナリ関数の堎合を介しお型を芋぀けようずしたす。



     struct collider_sh_as { typedef const char* result_type; typedef space_ship* first_argument_type; typedef asteroid* second_argument_type; ... }
          
          





    たたは、はるかに䟿利です



     struct collider_sh_as : std::binary_function<space_ship*, asteroid*, result_type> { ... }
          
          





    したがっお、std :: unary_function、std :: binary_functionは自動的にサポヌトされたす。



  4. 前の3぀のステップが機胜しなかった堎合、BOOST_TYPEOFが䜿甚されたす。 1぀の非暙準挔算子を持぀ファンクタヌがサポヌトされたす。 したがっお、std ::関数およびC ++ 11ラムダがサポヌトされたす。 䞀郚のコンパむラは、カスタム型を登録する必芁がありたす。 BOOST_TYPEOFがどのように機胜するかはただわかりたせん。ナヌザヌタむプを登録したせんでした。MSVC7.1以降でパラメヌタヌタむプを完党に衚瀺したす。


前の䟋では、マルチメ゜ッドは同じアリティの関数から組み立おられたした。 これはラむブラリの制限ではありたせん。

䞀般に、ラむブラリは0〜MML_MAX_ARITYのアリティをサポヌトしたす。 MML_MAX_ARITYのデフォルトは5です。デフォルト倀は倉曎できたす。 これを行うには、ヘッダヌを接続する前に、マクロを定矩する必芁がありたす。



#define MML_MAX_ARITY n

nは最倧アリティです。



たずえば、ヌル、単項、䞉項関数を远加したす。



 //   , ,     const char* collide_void() { return "Nothing collides?!"; } //     ,   const char* collide_go(game_object*) { return "Unsupported colliding!"; } const char* collide_sh(space_ship*) { return "Space ship collides with what?!"; } const char* collide_as(asteroid*) { return "Asteroid collides with what?!"; } const char* collide_go_go_go(game_object*, game_object*, game_object*) { return "Unsupported colliding!"; } const char* collide_sh_as_as(space_ship*, asteroid*, asteroid*) { return "Space ship collides with two asteroids"; } const char* collide_sh_as_st(space_ship*, asteroid*, space_station*) { return "Space ship collides with asteroid and space_station"; }
      
      





異なる数のオブゞェクトをプッシュする新しいテスタヌが必芁です。



 template <typename F, typename Objs> void collide_tester_var_arg(F collide, Objs& objs) { //   cout << '\t' << collide() << endl; // 1  cout << '\t' << collide(objs[0]) << endl; cout << '\t' << collide(objs[1]) << endl; // 2  cout << '\t' << collide(objs[0], objs[0]) << endl; cout << '\t' << collide(objs[0], objs[1]) << endl; // 3  cout << '\t' << collide(objs[0], objs[1], objs[1]) << endl; cout << '\t' << collide(objs[0], objs[1], objs[2]) << endl; }
      
      





マルチメ゜ッドを䜜成しお䜿甚したす。



 collide_tester_var_arg( make_multimethod( collide_go_go , collide_sh_sh , collide_sh_as , collide_void , collide_go , collide_sh , collide_as , collide_go_go_go , collide_sh_as_as , collide_sh_as_st ) , get_obj_pointers() );
      
      





おわりに
  Nothing collides Space ship collides with what?! Asteroid collides with what?! Space ship collides with space ship Space ship collides with asteroid Space ship collides with two asteroids Space ship collides with asteroid and space_station
      
      





したがっお、マルチメ゜ッドは、匕数の数に関しお静的にオヌバヌロヌドされた関数のように動䜜したす。

擬䌌コヌドのレント盞圓はここでより興味深いものになりたす、それは匕数の数に匷く䟝存したす



擬䌌コヌド
 // collide() inline const char* collide() { return collide_void(); } //     ! // collide(objs[0]) // collide(objs[1]) inline const char* collide(game_object* obj) { if (space_ship* sh = dynamic_cast<space_ship*>(obj)) return collide_sh(sh); else if (asteroid* as = dynamic_cast<asteroid*>(obj)) return collide_as(as); else return collide_go(obj); } //    : // min 1 cast // max 2 casts // collide(objs[0], objs[0]) // collide(objs[0], objs[1]) //  ,      : inline const char* collide(game_object* obj1, game_object* obj2) {...} // collide(objs[0], objs[1], objs[1]) // collide(objs[0], objs[1], objs[2]) inline const char* collide(game_object* obj1, game_object* obj2, game_object* obj3) { if (space_ship* sh1 = dynamic_cast<space_ship*>(obj1)) if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(sh1, sh2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(sh1, sh2, st3); else return collide_go_go_go(sh1, sh2, obj3); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_sh_as_as(sh1, as2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_sh_as_st(sh1, as2, st3); else return collide_go_go_go(sh1, as2, obj3); else if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(sh1, obj2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(sh1, obj2, st3); else return collide_go_go_go(sh1, obj2, obj3); else if (asteroid* as1 = dynamic_cast<asteroid*>(obj1)) if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(as1, sh2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(as1, sh2, st3); else return collide_go_go_go(as1, sh2, obj3); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(as1, as2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(as1, as2, st3); else return collide_go_go_go(as1, as2, obj3); else if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(as1, obj2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(as1, obj2, st3); else return collide_go_go_go(as1, obj2, obj3); else if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(obj1, sh2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(obj1, sh2, st3); else return collide_go_go_go(obj1, sh2, obj3); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(obj1, as2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(obj1, as2, st3); else return collide_go_go_go(obj1, as2, obj3); else if (asteroid* as3 = dynamic_cast<asteroid*>(obj3)) return collide_go_go_go(obj1, obj2, as3); else if (space_station* st3 = dynamic_cast<space_station*>(obj3)) return collide_go_go_go(obj1, obj2, st3); else return collide_go_go_go(obj1, obj2, obj3); } //    : // min 3 casts // max 6 casts
      
      





倚盞型ず倀型を混圚させるこずができたす。 倀型はそのたた枡されたす。

int型の3番目のパラメヌタヌを受け取るこずができるいく぀かの远加の目的関数を玹介したす。



 const char* collide_go_go_int(game_object*, game_object*, int) { return "Unsupported colliding with extra int parameter!"; } const char* collide_sh_as_int(space_ship*, asteroid*, int) { return "Space ship collides with asteroid with extra int parameter"; }
      
      





明確にするために、テスタヌはマルチメ゜ッドに送信したす。

2぀の倚盞型;

3ポリモヌフィックタむプ;

2ポリモヌフィックず1぀の非ポリモヌフィックタむプ



 template <typename F, typename Objs> void collide_tester_non_polymorphic_arg(F collide, Objs& objs) { cout << '\t' << collide(objs[0], objs[1]) << endl; cout << '\t' << collide(objs[0], objs[1], objs[2]) << endl; cout << '\t' << collide(objs[0], objs[1], 1) << endl; }
      
      





以前のタヌゲット関数ず新しいタヌゲット関数に基づいおマルチメ゜ッドを䜜成し、以䞋を䜿甚したす。



 collide_tester_non_polymorphic_arg( make_multimethod( collide_go_go , collide_sh_sh , collide_sh_as , collide_as_sh , collide_as_as , collide_go_go_go , collide_sh_as_st , collide_sh_as_as , collide_go_go_int , collide_sh_as_int ) , get_obj_pointers() );
      
      





おわりに
  Space ship collides with asteroid Space ship collides with asteroid and space_station Space ship collides with asteroid with extra int parameter
      
      





最初の呌び出しず2番目の呌び出しに盞圓するRanthimeを指定したした。



擬䌌コヌド
 // collide(objs[0], objs[1], 1) inline const char* collide(game_object* obj1, game_object* obj2, int n) { if (space_ship* sh1 = dynamic_cast<space_ship*>(obj1)) if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) return collide_go_go_int(sh1, sh2, n); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) return collide_sh_as_int(sh1, as2, n); else return collide_go_go_int(sh1, obj2, n); else if (asteroid* as1 = dynamic_cast<asteroid*>(obj1)) if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) return collide_go_go_int(as1, sh2, n); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) return collide_go_go_int(as1, as2, n); else return collide_go_go_int(as1, obj2, n); else if (space_ship* sh2 = dynamic_cast<space_ship*>(obj2)) return collide_go_go_int(obj1, sh2, n); else if (asteroid* as2 = dynamic_cast<asteroid*>(obj2)) return collide_go_go_int(obj1, as2, n); else return collide_go_go_int(obj1, obj2, n); } //    : // min 2 casts // max 4 casts
      
      





ご芧のずおり、非倚態的な匕数は远加のオヌバヌヘッドに぀ながりたせん。



constおよびvolatile修食子のオヌバヌロヌドもサポヌトされおいたす。



 const char* collide_go_cvgo(game_object*, const volatile game_object*) { return "Unsupported colliding!"; } const char* collide_sh_cas(space_ship*, const asteroid*) { return "Space ship collides with const asteroid"; } const char* collide_sh_vas(space_ship*, volatile asteroid*) { return "Space ship collides with volatile asteroid"; } const char* collide_sh_cvas(space_ship*, const volatile asteroid*) { return "Space ship collides with const volatile asteroid"; } template <typename F, typename Objs> void collide_tester_cv(F collide, Objs& objs) { game_object* object = objs[1]; const game_object* c_object = objs[1]; const volatile game_object* cv_object = objs[1]; cout << '\t' << collide(objs[0], object) << endl; cout << '\t' << collide(objs[0], c_object) << endl; cout << '\t' << collide(objs[0], cv_object) << endl; } collide_tester_cv( make_multimethod( collide_sh_as , collide_go_cvgo , collide_sh_cas , collide_sh_vas , collide_sh_cvas ) , get_obj_pointers() ); }
      
      





おわりに
  Space ship collides with asteroid Space ship collides with const asteroid Space ship collides with const volatile asteroid
      
      





マルチメ゜ッドの重芁な機胜は、倚盞匕数の正確なタむプがコンパむル段階ですでにわかっおいる堎合にスケゞュヌリングを最適化する機胜です。぀たり、実際のタむプの匕数の怜玢は、階局ノヌドから始たりたす。これは、枡される匕数の実際のタむプです。説明が簡単になりたす...䟋を芋おみたしょう



 template <typename F, typename Objs> void collide_tester_compile_time_optim(F collide, Objs& objs) { space_ship ship; asteroid ast; cout << '\t' << collide(objs[0], &ast) << endl; cout << '\t' << collide(&ship, &ast) << endl; } collide_tester_compile_time_optim( make_multimethod( collide_go_go , collide_sh_sh , collide_sh_as , collide_as_sh , collide_as_as ) , get_obj_pointers() );
      
      





おわりに
  Space ship collides with asteroid Space ship collides with asteroid
      
      





次に、同等のランタむムを慎重に怜蚎したす。



 // collide(objs[0], &ast) inline const char* collide(game_object* obj1, asteroid* as2) { if (space_ship* sh1 = dynamic_cast<space_ship*>(obj1)) return collide_sh_as(sh1, as2); else if (asteroid* as1 = dynamic_cast<asteroid*>(obj1)) return collide_as_as(as1, as2); else return collide_go_go(obj1, as2); } //    : // min 1 cast // max 2 casts // collide(&ship, &ast) inline const char* collide(space_ship* sh1, asteroid* as2) { return collide_sh_as(sh1, as2); } //   !
      
      





最初の呌び出しは最初の匕数によっおのみディスパッチされ、2番目の匕数はそのたた枡されたす。その型はコンパむル時に確実に既知です。

通垞、2番目の呌び出しは単玔な転送であり、䞡方の匕数の型はコンパむル時に既知です。

そのため、このような最適化をサポヌトするために、可胜であれば、マルチメ゜ッドを玔粋な圢匏で栌玍および送信し、boost :: function / * std :: function * /の埌ろに隠れおはなりたせん。アップキャストを䌎う呌び出し-衝突game_object *ship、game_object *ast-ラむブラリから最適化の可胜性を奪いたす。



マルチメ゜ッドは、ベアポむンタヌだけでなく、boost :: shared_ptr、およびテンプレヌトのペアを適切に特化したカスタムポむンタヌ型もサポヌトしおいたす詳现に぀いおは、次の蚘事を参照。boost :: shared_ptrをサポヌトするには、ヘッダヌファむル<mml / casting / sp_dynamic_caster.hpp>を含める必芁がありたす。

䟋



 #include <mml/casting/sp_dynamic_caster.hpp> const char* sp_collide_go_go(game_object*, boost::shared_ptr<game_object>) { return "Unsupported colliding!"; } const char* sp_collide_sh_sh(space_ship*, boost::shared_ptr<space_ship>) { return "Space ship collides with smart_ptr space ship"; } struct sp_collider_sh_as { const char* operator()(space_ship*, boost::shared_ptr<asteroid>) const { return "Space ship collides with smart_ptr asteroid"; } }; template <typename F, typename Objs> void sp_collide_tester(F collide, Objs& objs) { boost::shared_ptr<game_object> obj1(new space_ship); boost::shared_ptr<game_object> obj2(new asteroid); cout << '\t' << collide(objs[0], obj1) << endl; cout << '\t' << collide(objs[0], obj2) << endl; } sp_collide_tester( make_multimethod( sp_collide_go_go , sp_collide_sh_sh , sp_collider_sh_as() ) , get_obj_pointers() );
      
      





おわりに
  Space ship collides with smart_ptr space ship Space ship collides with smart_ptr asteroid
      
      





Ranthimeの同等物



擬䌌コヌド
 // collide(objs[0], obj1) // collide(objs[0], obj2) inline const char* collide(game_object* obj1, const boost::shared_ptr<game_object>& sp_obj2) { if (space_ship* sh1 = dynamic_cast<space_ship*>(obj1)) if (boost::shared_ptr<space_ship> sp_sh2 = boost::shared_dynamic_cast<space_ship>(sp_obj2)) return sp_collide_sh_sh(sh1, sp_sh2); else if (boost::shared_ptr<asteroid> sp_as2 = boost::shared_dynamic_cast<asteroid>(sp_obj2)) return sp_collider_sh_as()(sh1, sp_as2); else return sp_collide_go_go(obj1, sp_obj2); else if (boost::shared_ptr<space_ship> sp_sh2 = boost::shared_dynamic_cast<space_ship>(sp_obj2)) return sp_collide_go_go(obj1, sp_sh2); else if (boost::shared_ptr<asteroid> sp_as2 = boost::shared_dynamic_cast<asteroid>(sp_obj2)) return sp_collide_go_go(obj1, sp_as2); else return sp_collide_go_go(obj1, sp_obj2); } //    : // min 2 casts // max 3 casts
      
      





䟋の゜ヌスはこちらです。



メリット

. . , , . . . , .

( 0).

— compile time, .



短所

( ). , .. . - . , . , .

, .



性胜

— dynamic_cast. dynamic_cast . , - . , «fast_cast», dynamic_cast . , . , ?

GUI . (inlining) .

, . Acyclyc Visitor , . dynamic_cast.

, , . ( , ) — .



䟝存関係

C++ 03 BOOST (headers only) 1.45.0 .



重芁コンパむラむンクルヌドディレクトリ内に「boost」および「mml」ディレクトリが存圚する必芁がありたす。「boost」および「mml」ディレクトリ自䜓をincludeディレクトリずしお指定せず、それらを含むディレクトリを指定したす。



サンプル付きのラむブラリは、コンパむラでテストされおいたすこれたではWindowsでのみ

。GCC3.4.2。

Visual C ++ 7.1、8.0、9.0、10.0



UPD , , . , . , ( 57 ~190 ). , . , , if-else, .



UPD 2: : , C++ . , . , . mejedi .



. , .



All Articles