Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Multiple dispatch
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==== C++ ==== {{As of|2021}}, [[C++]] natively supports only single dispatch, though adding multi-methods (multiple dispatch) was proposed by [[Bjarne Stroustrup]] (and collaborators) in 2007.<ref>{{Cite web|last=|first=|date=2007-03-11|title=Report on language support for Multi-Methods and Open-Methods for C ++|url=http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2216.pdf|quote=Multiple dispatch β the selection of a function to be invoked based on the dynamic type of two or more arguments β is a solution to several classical problems in object-oriented programming.|archive-url=|archive-date=|access-date=|website=}}</ref> The methods of working around this limit are analogous: use either the [[visitor pattern]], dynamic cast or a library: <syntaxhighlight lang="cpp"> // Example using run time type comparison via dynamic_cast struct Thing { virtual void collideWith(Thing& other) = 0; }; struct Asteroid : Thing { void collideWith(Thing& other) { // dynamic_cast to a pointer type returns NULL if the cast fails // (dynamic_cast to a reference type would throw an exception on failure) if (auto asteroid = dynamic_cast<Asteroid*>(&other)) { // handle Asteroid-Asteroid collision } else if (auto spaceship = dynamic_cast<Spaceship*>(&other)) { // handle Asteroid-Spaceship collision } else { // default collision handling here } } }; struct Spaceship : Thing { void collideWith(Thing& other) { if (auto asteroid = dynamic_cast<Asteroid*>(&other)) { // handle Spaceship-Asteroid collision } else if (auto spaceship = dynamic_cast<Spaceship*>(&other)) { // handle Spaceship-Spaceship collision } else { // default collision handling here } } }; </syntaxhighlight> or pointer-to-method lookup table: <syntaxhighlight lang="cpp"> #include <cstdint> #include <typeinfo> #include <unordered_map> class Thing { protected: Thing(std::uint32_t cid) : tid(cid) {} const std::uint32_t tid; // type id typedef void (Thing::*CollisionHandler)(Thing& other); typedef std::unordered_map<std::uint64_t, CollisionHandler> CollisionHandlerMap; static void addHandler(std::uint32_t id1, std::uint32_t id2, CollisionHandler handler) { collisionCases.insert(CollisionHandlerMap::value_type(key(id1, id2), handler)); } static std::uint64_t key(std::uint32_t id1, std::uint32_t id2) { return std::uint64_t(id1) << 32 | id2; } static CollisionHandlerMap collisionCases; public: void collideWith(Thing& other) { auto handler = collisionCases.find(key(tid, other.tid)); if (handler != collisionCases.end()) { (this->*handler->second)(other); // pointer-to-method call } else { // default collision handling } } }; class Asteroid: public Thing { void asteroid_collision(Thing& other) { /*handle Asteroid-Asteroid collision*/ } void spaceship_collision(Thing& other) { /*handle Asteroid-Spaceship collision*/} public: Asteroid(): Thing(cid) {} static void initCases(); static const std::uint32_t cid; }; class Spaceship: public Thing { void asteroid_collision(Thing& other) { /*handle Spaceship-Asteroid collision*/} void spaceship_collision(Thing& other) { /*handle Spaceship-Spaceship collision*/} public: Spaceship(): Thing(cid) {} static void initCases(); static const std::uint32_t cid; // class id }; Thing::CollisionHandlerMap Thing::collisionCases; const std::uint32_t Asteroid::cid = typeid(Asteroid).hash_code(); const std::uint32_t Spaceship::cid = typeid(Spaceship).hash_code(); void Asteroid::initCases() { addHandler(cid, cid, CollisionHandler(&Asteroid::asteroid_collision)); addHandler(cid, Spaceship::cid, CollisionHandler(&Asteroid::spaceship_collision)); } void Spaceship::initCases() { addHandler(cid, Asteroid::cid, CollisionHandler(&Spaceship::asteroid_collision)); addHandler(cid, cid, CollisionHandler(&Spaceship::spaceship_collision)); } int main() { Asteroid::initCases(); Spaceship::initCases(); Asteroid a1, a2; Spaceship s1, s2; a1.collideWith(a2); a1.collideWith(s1); s1.collideWith(s2); s1.collideWith(a1); } </syntaxhighlight> The ''YOMM2'' library<ref name="yomm2">[https://github.com/jll63/yomm2 yomm2], Fast, Orthogonal Open Multi-Methods for C++ by Jean-Louis Leroy.</ref> provides a fast, orthogonal implementation of open multimethods. The syntax for declaring open methods is inspired by a proposal for a native C++ implementation. The library requires that the user registers all the classes used as virtual arguments (and their sub-classes), but does not require any modifications to existing code. Methods are implemented as ordinary inline C++ functions; they can be overloaded and they can be passed by pointer. There is no limit on the number of virtual arguments, and they can be arbitrarily mixed with non-virtual arguments. The library uses a combination of techniques (compressed dispatch tables, collision free integer hash table) to implement method calls in constant time, while mitigating memory usage. Dispatching a call to an open method with a single virtual argument takes only 15β30% more time than calling an ordinary virtual member function, when a modern optimizing compiler is used. The Asteroids example can be implemented as follows: <syntaxhighlight lang="cpp"> #include <yorel/yomm2/keywords.hpp> #include <memory> class Thing { public: virtual ~Thing() {} }; class Asteroid : public Thing { }; class Spaceship : public Thing { }; register_classes(Thing, Spaceship, Asteroid); declare_method(void, collideWith, (virtual_<Thing&>, virtual_<Thing&>)); define_method(void, collideWith, (Thing& left, Thing& right)) { // default collision handling } define_method(void, collideWith, (Asteroid& left, Asteroid& right)) { // handle Asteroid-Asteroid collision } define_method(void, collideWith, (Asteroid& left, Spaceship& right)) { // handle Asteroid-Spaceship collision } define_method(void, collideWith, (Spaceship& left, Asteroid& right)) { // handle Spaceship-Asteroid collision } define_method(void, collideWith, (Spaceship& left, Spaceship& right)) { // handle Spaceship-Spaceship collision } int main() { yorel::yomm2::update_methods(); std::unique_ptr<Thing> a1(std::make_unique<Asteroid>()), a2(std::make_unique<Asteroid>()); std::unique_ptr<Thing> s1(std::make_unique<Spaceship>()), s2(std::make_unique<Spaceship>()); // note: types partially erased collideWith(*a1, *a2); // Asteroid-Asteroid collision collideWith(*a1, *s1); // Asteroid-Spaceship collision collideWith(*s1, *a1); // Spaceship-Asteroid collision collideWith(*s1, *s2); // Spaceship-Spaceship collision return 0; } </syntaxhighlight> Stroustrup mentions in ''The Design and Evolution of C++'' that he liked the concept of multimethods and considered implementing it in C++ but claims to have been unable to find an efficient sample implementation (comparable to virtual functions) and resolve some possible type ambiguity problems. He then states that although the feature would still be nice to have, that it can be approximately implemented using [[double dispatch]] or a type based lookup table as outlined in the C/C++ example above so is a low priority feature for future language revisions.<ref>{{cite book |last=Stroustrup |first=Bjarne |title=The Design and Evolution of C++ |publisher=Addison Wesley |location=Indianapolis, IN, U.S.A |year=1994 |chapter=Section 13.8 |isbn=978-0-201-54330-8|bibcode=1994dec..book.....S }}</ref>
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)