// Copyright (c) 2018-2025 Jean-Louis Leroy // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) // NOTE: No actual animals were hurt while designing, coding, compiling and // running this example. // ============================================================================= // Define a few polymorphic classes... class Animal { public: virtual ~Animal() { } }; class Dog : public Animal {}; class Bulldog : public Dog {}; class Cat : public Animal {}; class Dolphin : public Animal {}; // ============================================================================= // Add behavior to existing classes, without modifying them. #include #include using boost::openmethod::virtual_ptr; // Classes must be registered: BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, Dolphin); // ...but it does not have to be in one call to 'BOOST_OPENMETHOD_CLASSES', as long as // inheritance relationships can be deduced. This allows *adding* classes to an // existing collection of classes. BOOST_OPENMETHOD_CLASSES(Dog, Bulldog); // Define a uni-method, i.e. a method with a single virtual argument. This is in // essence a virtual function implemented as a free function. BOOST_OPENMETHOD(poke, (virtual_ptr, std::ostream&), void); // Implement 'poke' for dogs. BOOST_OPENMETHOD_OVERRIDE( poke, (virtual_ptr /*dog*/, std::ostream& os), void) { os << "bark"; } // Implement 'poke' for bulldogs. They behave like Dogs, but, in addition, they // fight back. BOOST_OPENMETHOD_OVERRIDE( poke, (virtual_ptr dog, std::ostream& os), void) { next(dog, os); // calls "base" method, i.e. definition for Dog os << " and bite"; } // A multi-method with two virtual arguments... BOOST_OPENMETHOD( meet, (virtual_ptr, virtual_ptr, std::ostream&), void); // 'meet' catch-all implementation. BOOST_OPENMETHOD_OVERRIDE( meet, (virtual_ptr, virtual_ptr, std::ostream& os), void) { os << "ignore"; } // Add definitions for specific pairs of animals. BOOST_OPENMETHOD_OVERRIDE( meet, (virtual_ptr /*dog1*/, virtual_ptr /*dog2*/, std::ostream& os), void) { os << "wag tail"; } BOOST_OPENMETHOD_OVERRIDE( meet, (virtual_ptr /*dog*/, virtual_ptr /*cat*/, std::ostream& os), void) { os << "chase"; } BOOST_OPENMETHOD_OVERRIDE( meet, (virtual_ptr /*cat*/, virtual_ptr /*dog*/, std::ostream& os), void) { os << "run"; } // ============================================================================= // main #include #include #include auto main() -> int { // Initialize the dispatch tables. boost::openmethod::initialize(boost::openmethod::trace::from_env()); // Create a few objects. // Note that the actual classes are type-erased to base class Animal! std::unique_ptr hector = std::make_unique(), snoopy = std::make_unique(), sylvester = std::make_unique(), flipper = std::make_unique(); // Call 'poke'. std::cout << "poke snoopy: "; poke(*snoopy, std::cout); // bark std::cout << "\n"; std::cout << "poke hector: "; poke(*hector, std::cout); // bark and bite std::cout << "\n"; // Call 'meet'. std::cout << "hector meets sylvester: "; meet(*hector, *sylvester, std::cout); // chase std::cout << "\n"; std::cout << "sylvester meets hector: "; meet(*sylvester, *hector, std::cout); // run std::cout << "\n"; std::cout << "hector meets snoopy: "; meet(*hector, *snoopy, std::cout); // wag tail std::cout << "\n"; std::cout << "hector meets flipper: "; meet(*hector, *flipper, std::cout); // ignore std::cout << "\n"; } // Let's look at the code generated by clang++-14 for method call. void call_poke(Animal& a, std::ostream& os) { poke(a, os); // Instructions in the same paragraph are independent, thus they can be // executed in parallel. // mov rax, qword ptr [rdi] ; read vptr // mov rdx, qword ptr [rip + global+24] ; M hash factor (multiply) // imul rdx, qword ptr [rax - 8] ; multiply vptr[-1] (&typeid(a)) by M // mov cl, byte ptr [rip + global+32] ; S hash factor (shift) // shr rdx, cl ; shift by S: this is the position of // ; the method table for the dynamic class of 'a' // ; in the global hash table // mov rax, qword ptr [rip + global+40] ; address of global hash table // mov rax, qword ptr [rax + 8*rdx] ; method table for the class // mov rcx, qword ptr [rip + method+96] ; offset of the 'poke' in method table // mov rax, qword ptr [rax + 8*rcx] ; read function pointer at offset // jmp rax ; tail call } void call_meet(Animal& a, Animal& b, std::ostream& os) { meet(a, b, os); // Instructions in the same paragraph are independent, thus they can be // executed in parallel. // mov r8, qword ptr [rdi] ; vptr of 'a' // mov r9, qword ptr [rip + global+24] ; M hash factor (multiply) // mov cl, byte ptr [rip + global+32] ; S hash factor (shift) // mov r10, qword ptr [r8 - 8] ; read a.vptr[-1] (&a) // imul r10, r9 ; multiply by M // shr r10, cl ; index of method table for 'a' // mov r8, qword ptr [rip + global+40] ; address of global hash table // mov rax, qword ptr [rsi] ; read vptr of 'b' // imul r9, qword ptr [rax - 8] ; multiply b.vptr[-1] (&typeid(b)) by M // mov rax, qword ptr [r8 + 8*r10] ; method table for 'a' // shr r9, cl ; index of method table for 'b' // mov rcx, qword ptr [rip + method+96] ; offset of 'meet' in method table for 'a' // mov r10, qword ptr [rax + 8*rcx] ; pointer to row for 'a' in dispatch table // mov rcx, qword ptr [r8 + 8*r9] ; method table for 'b' // mov rax, qword ptr [rip + method+104] ; offset of 'meet' in method table for 'b' // mov rax, qword ptr [rcx + 8*rax] ; column of 'b' in dispatch table // imul rax, qword ptr [rip + method+112] ; multiply by # of lines in dispatch table // mov rax, qword ptr [r10 + 8*rax] ; pointer to function, at dispatch[a][b] // jmp rax ; tail call }