mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 16:32:12 +00:00
197 lines
6.7 KiB
C++
197 lines
6.7 KiB
C++
// 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 <boost/openmethod.hpp>
|
|
#include <boost/openmethod/initialize.hpp>
|
|
|
|
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<Animal>, std::ostream&), void);
|
|
|
|
// Implement 'poke' for dogs.
|
|
BOOST_OPENMETHOD_OVERRIDE(
|
|
poke, (virtual_ptr<Dog> /*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<Bulldog> 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<Animal>, virtual_ptr<Animal>, std::ostream&), void);
|
|
|
|
// 'meet' catch-all implementation.
|
|
BOOST_OPENMETHOD_OVERRIDE(
|
|
meet, (virtual_ptr<Animal>, virtual_ptr<Animal>, std::ostream& os), void) {
|
|
os << "ignore";
|
|
}
|
|
|
|
// Add definitions for specific pairs of animals.
|
|
BOOST_OPENMETHOD_OVERRIDE(
|
|
meet,
|
|
(virtual_ptr<Dog> /*dog1*/, virtual_ptr<Dog> /*dog2*/, std::ostream& os),
|
|
void) {
|
|
os << "wag tail";
|
|
}
|
|
|
|
BOOST_OPENMETHOD_OVERRIDE(
|
|
meet,
|
|
(virtual_ptr<Dog> /*dog*/, virtual_ptr<Cat> /*cat*/, std::ostream& os),
|
|
void) {
|
|
os << "chase";
|
|
}
|
|
|
|
BOOST_OPENMETHOD_OVERRIDE(
|
|
meet,
|
|
(virtual_ptr<Cat> /*cat*/, virtual_ptr<Dog> /*dog*/, std::ostream& os),
|
|
void) {
|
|
os << "run";
|
|
}
|
|
|
|
// =============================================================================
|
|
// main
|
|
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
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<Animal> hector = std::make_unique<Bulldog>(),
|
|
snoopy = std::make_unique<Dog>(),
|
|
sylvester = std::make_unique<Cat>(),
|
|
flipper = std::make_unique<Dolphin>();
|
|
|
|
// 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
|
|
}
|