Files
openmethod/doc/modules/ROOT/examples/synopsis.cpp
2025-09-28 16:08:11 -04:00

197 lines
6.6 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<>();
// 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
}