This commit is contained in:
Jean-Louis Leroy
2025-10-13 20:08:46 -04:00
parent 1b94d2948e
commit 626c74960a
38 changed files with 1018 additions and 368 deletions

View File

@@ -26,8 +26,17 @@ foreach (cpp ${cpp_files})
add_dependencies(tests ${stem})
endforeach()
add_subdirectory(headers_namespaces)
if (NOT WIN32)
add_subdirectory(shared_libs)
endif()
file(GLOB subdirs "rolex/*")
foreach (subdir ${subdirs})
string(REGEX REPLACE ".*/" "" subex ${subdir})
file(GLOB cpp_files "${subdir}/*.cpp")
add_executable("rolex_${subex}" ${cpp_files})
target_link_libraries("rolex_${subex}" PUBLIC Boost::openmethod)
add_test(NAME "rolex_${subex}" COMMAND "rolex_${subex}")
add_dependencies(tests "rolex_${subex}")
endforeach()

View File

@@ -29,6 +29,12 @@ struct Times : Node {
const Node& left; const Node& right;
};
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// library code
// =============================================================================
// application code
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <iostream>
@@ -67,6 +73,7 @@ int main() {
postfix(e, std::cout);
std::cout << " = " << e.value() << "\n"; // 2 3 + 4 * = 20
}
// end::content[]
void call_via_ref(const Node& node, std::ostream& os) {
postfix(node, os);

View File

@@ -11,9 +11,6 @@
using namespace boost::openmethod::aliases;
// NOTE: unique_virtual_ptr<Class> is an alias for
// virtual_ptr<std::unique_ptr<Class>>
struct Node {
virtual ~Node() {}
virtual int value() const = 0;
@@ -70,11 +67,13 @@ BOOST_OPENMETHOD_CLASSES(Node, Variable, Plus, Times);
int main() {
boost::openmethod::initialize();
auto a = std::make_unique<Variable>(2);
auto b = std::make_unique<Variable>(3);
auto c = std::make_unique<Variable>(4);
auto d = make_unique_virtual<Plus>(std::move(a), std::move(b));
auto e = make_unique_virtual<Times>(std::move(d), std::move(c));
postfix(e, std::cout);
std::cout << " = " << e->value() << "\n"; // 2 3 + 4 * = 20
}

View File

@@ -39,7 +39,7 @@ class Animal {
class Cat;
class Dog;
template<typename> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
class Animal {
// ...

View File

@@ -43,7 +43,7 @@ BOOST_OPENMETHOD(poke, (std::ostream&, virtual_ptr<Animal>), void);
namespace pets {
struct Cat;
struct Dog;
template<typename> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(poke);
} // namespace pets
namespace core {

View File

@@ -0,0 +1,16 @@
// 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)
// tag::content[]
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
return 5000.0;
}
BOOST_OPENMETHOD_CLASSES(Employee)
// end::content[]

View File

@@ -0,0 +1,23 @@
// 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)
// tag::content[]
// main.cpp
#include "roles.hpp"
#include <iostream>
#include <boost/openmethod/initialize.hpp>
int main() {
boost::openmethod::initialize();
Employee bill;
Salesman bob; bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]

View File

@@ -0,0 +1,23 @@
// 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)
// tag::content[]
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
struct Employee { virtual ~Employee() = default; };
struct Salesman : Employee {
double sales = 0.0;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
#endif // ROLES_HPP
// end::content[]

View File

@@ -0,0 +1,17 @@
// 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)
// tag::content[]
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
return next(emp) + emp->sales * 0.05; // base + commission
}
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
// end::content[]

View File

@@ -0,0 +1,16 @@
// 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)
// tag::content[]
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_DEFINE_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
return 5000.0;
}
BOOST_OPENMETHOD_CLASSES(Employee)
// end::content[]

View File

@@ -0,0 +1,23 @@
// 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)
// tag::content[]
// main.cpp
#include "roles.hpp"
#include <iostream>
#include <boost/openmethod/initialize.hpp>
int main() {
boost::openmethod::initialize();
Employee bill;
Salesman bob; bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]

View File

@@ -0,0 +1,27 @@
// 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)
// tag::content[]
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
struct Employee { virtual ~Employee() = default; };
struct Salesman : Employee {
double sales = 0.0;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const Employee>), double);
#endif // ROLES_HPP
// end::content[]

View File

@@ -0,0 +1,19 @@
// 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)
#include "roles.hpp"
#include <boost/openmethod.hpp>
// tag::content[]
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
return BOOST_OPENMETHOD_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const Employee> emp),
double)::fn(emp) +
emp->sales * 0.05; // base + commission
}
// end::content[]
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)

View File

@@ -0,0 +1,11 @@
// 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)
// tag::content[]
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_CLASSES(Employee)
// end::content[]

View File

@@ -0,0 +1,23 @@
// 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)
// tag::content[]
// main.cpp
#include "roles.hpp"
#include <iostream>
#include <boost/openmethod/initialize.hpp>
int main() {
boost::openmethod::initialize();
Employee bill;
Salesman bob; bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]

View File

@@ -0,0 +1,28 @@
// 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)
// tag::content[]
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
struct Employee { virtual ~Employee() = default; };
struct Salesman : Employee {
double sales = 0.0;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
BOOST_OPENMETHOD_INLINE_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
return 5000.0;
}
#endif // ROLES_HPP
// end::content[]

View File

@@ -0,0 +1,19 @@
// 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)
#include "roles.hpp"
#include <boost/openmethod.hpp>
// tag::content[]
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
return BOOST_OPENMETHOD_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const Employee> emp),
double)::fn(emp) +
emp->sales * 0.05; // base + commission
}
// end::content[]
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)

View File

@@ -0,0 +1,11 @@
// 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)
// tag::content[]
#include "roles.hpp"
#include <boost/openmethod.hpp>
BOOST_OPENMETHOD_CLASSES(employees::Employee)
// end::content[]

View File

@@ -0,0 +1,23 @@
// 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)
// tag::content[]
// main.cpp
#include "roles.hpp"
#include <iostream>
#include <boost/openmethod/initialize.hpp>
int main() {
boost::openmethod::initialize();
employees::Employee bill;
sales::Salesman bob; bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(bill) << "\n"; // pay bill: $5000
std::cout << "pay bob: $" << pay(bob) << "\n"; // pay bob: $10000
}
// end::content[]

View File

@@ -0,0 +1,37 @@
// 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)
// tag::content[]
// roles.hpp
#ifndef ROLES_HPP
#define ROLES_HPP
#include <boost/openmethod.hpp>
namespace employees {
struct Employee {
virtual ~Employee() = default;
};
BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr<const Employee>), double);
BOOST_OPENMETHOD_INLINE_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Employee>), double) {
return 5000.0;
}
}
namespace sales {
struct Salesman : employees::Employee {
double sales = 0.0;
};
} // namespace sales
#endif // ROLES_HPP
// end::content[]

View File

@@ -0,0 +1,23 @@
// 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)
#include "roles.hpp"
#include <boost/openmethod.hpp>
// tag::content[]
namespace sales {
BOOST_OPENMETHOD_OVERRIDE(
pay, (boost::openmethod::virtual_ptr<const Salesman> emp), double) {
return employees::BOOST_OPENMETHOD_OVERRIDER(
pay, (boost::openmethod::virtual_ptr<const employees::Employee> emp),
double)::fn(emp) +
emp->sales * 0.05; // base + commission
}
BOOST_OPENMETHOD_CLASSES(employees::Employee, Salesman)
} // namespace sales
// end::content[]

View File

@@ -0,0 +1,90 @@
// 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)
// tag::content[]
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <iostream>
class Payroll;
struct Employee {
virtual ~Employee() = default;
};
struct Salesman : Employee {
double sales = 0.0;
};
// tag::pay[]
BOOST_OPENMETHOD(
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double);
// end::pay[]
// tag::payroll[]
class Payroll {
public:
double balance() const {
return balance_;
}
private:
double balance_ = 1'000'000.0;
void update_balance(double amount) {
// throw if balance would become negative
balance_ += amount;
}
friend BOOST_OPENMETHOD_OVERRIDER(
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double);
friend BOOST_OPENMETHOD_OVERRIDER(
pay,
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman>),
double);
};
// end::payroll[]
// tag::overriders[]
BOOST_OPENMETHOD_OVERRIDE(
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double) {
double pay = 5000.0;
payroll.update_balance(-pay);
return pay;
}
BOOST_OPENMETHOD_OVERRIDE(
pay,
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman> emp),
double) {
double base = next(payroll, emp);
double commission = emp->sales * 0.05;
payroll.update_balance(-commission);
return base + commission;
}
// ...and let's not forget to register the classes
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
// end::overriders[]
// tag::main[]
int main() {
boost::openmethod::initialize();
Payroll payroll;
Employee bill;
Salesman bob;
bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(payroll, bill) << "\n"; // $5000
std::cout << "pay bob: $" << pay(payroll, bob) << "\n"; // 10000
std::cout << "remaining balance: $" << payroll.balance() << "\n"; // $985000
}
// end::main[]

View File

@@ -0,0 +1,85 @@
// 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)
// tag::content[]
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <iostream>
class Payroll;
struct Employee {
virtual ~Employee() = default;
};
struct Salesman : Employee {
double sales = 0.0;
};
// tag::pay[]
BOOST_OPENMETHOD(
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double);
// end::pay[]
// tag::payroll[]
class Payroll {
public:
double balance() const {
return balance_;
}
private:
double balance_ = 1'000'000.0;
void update_balance(double amount) {
// throw if balance would become negative
balance_ += amount;
}
template<typename...>
friend struct BOOST_OPENMETHOD_OVERRIDERS(pay);
};
// end::payroll[]
// tag::overriders[]
BOOST_OPENMETHOD_OVERRIDE(
pay, (Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double) {
double pay = 5000.0;
payroll.update_balance(-pay);
return pay;
}
BOOST_OPENMETHOD_OVERRIDE(
pay,
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman> emp),
double) {
double base = next(payroll, emp);
double commission = emp->sales * 0.05;
payroll.update_balance(-commission);
return base + commission;
}
// ...and let's not forget to register the classes
BOOST_OPENMETHOD_CLASSES(Employee, Salesman)
// end::overriders[]
// tag::main[]
int main() {
boost::openmethod::initialize();
Payroll payroll;
Employee bill;
Salesman bob;
bob.sales = 100'000.0;
std::cout << "pay bill: $" << pay(payroll, bill) << "\n"; // $5000
std::cout << "pay bob: $" << pay(payroll, bob) << "\n"; // 10000
std::cout << "remaining balance: $" << payroll.balance() << "\n"; // $985000
}
// end::main[]

View File

@@ -0,0 +1,90 @@
// 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)
// tag::content[]
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <iostream>
// tag::classes[]
struct Role {
virtual ~Role() = default;
};
struct Employee : Role {};
struct Salesman : Employee {};
struct Manager : Employee {};
struct Founder : Role {};
struct Expense {
virtual ~Expense() {
}
};
struct Public : Expense {};
struct Bus : Public {};
struct Metro : Public {};
struct Taxi : Expense {};
struct Jet : Expense {};
// end::classes[]
BOOST_OPENMETHOD_CLASSES(
Role, Employee, Manager, Founder, Expense, Public, Bus, Metro, Taxi, Jet);
// tag::approve[]
using boost::openmethod::virtual_ptr;
BOOST_OPENMETHOD(
approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
bool);
BOOST_OPENMETHOD_OVERRIDE(
approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
bool) {
return false;
}
BOOST_OPENMETHOD_OVERRIDE(
approve, (virtual_ptr<const Employee>, virtual_ptr<const Public>, double),
bool) {
return true;
}
BOOST_OPENMETHOD_OVERRIDE(
approve,
(virtual_ptr<const Manager>, virtual_ptr<const Taxi>, double amount),
bool) {
return amount < 100.0;
}
BOOST_OPENMETHOD_OVERRIDE(
approve, (virtual_ptr<const Founder>, virtual_ptr<const Expense>, double),
bool) {
return true;
}
// tag::main[]
int main() {
boost::openmethod::initialize();
Founder bill;
Employee bob;
Manager alice;
Bus bus;
Taxi taxi;
Jet jet;
std::cout << std::boolalpha;
std::cout << approve(bill, bus, 4.0) << "\n"; // true
std::cout << approve(bob, bus, 4.0) << "\n"; // true
std::cout << approve(bob, taxi, 36.0) << "\n"; // false
std::cout << approve(alice, taxi, 36.0) << "\n"; // true
std::cout << approve(alice, taxi, 2000.0) << "\n"; // false
std::cout << approve(bill, jet, 120'000.0) << "\n"; // true
std::cout << approve(bob, jet, 120'000.0) << "\n"; // false
std::cout << approve(alice, jet, 120'000.0) << "\n"; // false
}
// end::main[]

View File

@@ -2,7 +2,8 @@
* xref:basics.adoc[Basics]
* xref:performance.adoc[Performance]
* xref:smart_pointers.adoc[Smart Pointers]
* xref:headers_namespaces.adoc[Headers and Namespaces]
* xref:headers.adoc[Headers]
* xref:namespaces.adoc[Namespaces]
* xref:friendship.adoc[Friendship]
* xref:multiple_dispatch.adoc[Multiple Dispatch]
* Advanced Features
@@ -13,6 +14,6 @@
** xref:custom_rtti.adoc[Custom RTTI]
** xref:shared_libraries.adoc[Shared Libraries]
* Reference
** xref:headers.adoc[Headers]
** xref:macros.adoc[Macros]
** xref:ref_headers.adoc[Headers]
** xref:ref_macros.adoc[Macros]
** xref:reference:boost/openmethod.adoc[Namespace boost::openmethod]

View File

@@ -3,8 +3,8 @@
## Basics
An _open-method_ is a free-standing function that takes one or more _virtual_
_parameters_. When it is called, it forwards to an _overrider_ selected by
examining the dynamic type of the virtual parameters.
_parameters_. When it is called, it forwards to an _overrider_ selected from a
set by examining the dynamic type of the virtual parameters.
If this sounds like a virtual function, that's because because an open-method
with one virtual parameter is equivalent to a virtual function - with a big
@@ -69,8 +69,8 @@ constructors from plain references or pointers to an object, or from other
There are two more things we need to do.
OpenMethod is a library, not a compiler. It needs to be aware of all the classes
that may be used as virtual parameters, and in method calls, and their
OpenMethod is a library, not a compiler. It needs to be made aware of all the
classes that may be used as virtual parameters, and in method calls, and their
inheritance relationships. We do this using the
xref:BOOST_OPENMETHOD_CLASSES.adoc[BOOST_OPENMETHOD_CLASSES] macro:
@@ -83,9 +83,9 @@ direct base of a class must appear together with it in at least one call to
`BOOST_OPENMETHOD_CLASSES`. This enables the library to deduce the complete
inheritance lattice.
The constructs used in this example require the classes need to be polymorphic,
in the standard C++ sense, i.e. have at least one virtual function. The library
can also be used with non-polymorphic classes, with some restrictions.
The constructs used in this example require the classes to be polymorphic, in
the standard C++ sense, i.e. have at least one virtual function. The library can
also be used with non-polymorphic classes, with some restrictions.
Finally, we need to call `boost::openmethod::initialize()` before the first call
to an open-method. This builds the dispatch tables used during method calls. It

View File

@@ -1,31 +1,49 @@
:example: ../examples/rolex
## Friendship
We can use overrider containers to grant friendship to a specific overrider, or
to all the overriders of a method. The name of the container template is
returned by `BOOST_OPENMETHOD_OVERRIDERS`. The template argument for a
specialization is the signature of the overrider. For example, the overrider of
`poke` for `Cat` is:
Note;; This section uses overrider containers, described in the
xref:headers.adoc[Headers] section.
`friend` is a controversial feature. OpenMethod aims to interact well with
all of C++, as much as feasible for a library, and leave the user the choice of
using `friend`, or not.
Let's consider yet another variation of the `pay` example. This time, we want to
update a `balance` variable in a `Payroll` class, when an employee is paid. Thus
we pass the payroll object to the `pay` method:
[source,c++]
----
BOOST_OPENMETHOD_OVERRIDERS(poke)<
void(std::ostream& os, virtual_ptr<Cat> cat)>::fn;
include::{example}/5/main.cpp[tag=pay]
----
We can thus grant friendship to all the overriders of `poke`:
`BOOST_OPENMETHOD` declares an overrider container for `pay` in the current
namespace, even though it does not define any overrider by itself. We can thus
name the individual address containers in `friend` declarations. But note that
at this point, the containers have not been specialized yet! In particular, the
`fn` member function does not exist yet. Instead, we declare friendship to the
container itself:
[source,c++]
----
include::example$friendship.cpp[tag=friend_all]
include::{example}/5/main.cpp[tag=payroll]
----
Be aware, though, that the overriders of _any_ method called `poke` - with any
signature - are granted friendship.
We can also befriend individual overriders:
We can now implement the `pay` overriders:
[source,c++]
----
include::example$friendship.cpp[tag=friend]
include::{example}/5/main.cpp[tag=overriders]
----
We can also declare friendship _en_ _masse_:
[source,c++]
----
include::{example}/6/main.cpp[tag=payroll]
----
Note, however, that this makes all the overriders of _any_ `pay` method, with
any signature, in the current namespace, friends of `Payroll`. Unfortunately,
C++ does not currently allow partial specialization of friend declarations.

View File

@@ -1,111 +1,102 @@
[#headers]
= xref:headers.adoc[Headers]
:example: ../examples/rolex
{empty}
## Headers
## Headers for General Use
Typically, `BOOST_OPENMETHOD` go in headers, while
`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` go in
implementation files.
The following headers are sufficient for most basic uses of the library.
Let's use a payroll application as an example.
We have two roles: `employee` and `salesman`, and a `pay` method that
computes the monthly pay of an employee.
We want to override and call `pay` from from multiple translation units, so
we put it in a header:
* xref:#main[`boost/openmethod.hpp`] to define open-methods and overriders using
convenient macros.
[source,c++]
----
include::{example}/1/roles.hpp[tag=content]
----
* xref:#initialize[`boost/openmethod/initialize.hpp`] to initialize the library.
Typically only included in the translation unit containing `main`.
`BOOST_OPENMETHOD` _defines_ an inline function, so it can be called only
once in a translation unit.
The include guards see to this.
The following headers make it possible to use standard smart pointers in virtual
parameters:
Let's write the override for "just" employees - they get a fixed salary.
`BOOST_OPENMETHOD_OVERRIDE` _adds_ an overrider to the method.
We don't want to add the same multiple times; that would create an
ambiguity.
Thus it should go in an implementation file:
* xref:#std_shared_ptr[`boost/openmethod/interop/std_shared_ptr.hpp`] to use
`std::shared_ptr` in virtual parameters.
[source,c++]
----
include::{example}/1/employee.cpp[tag=content]
----
* xref:#std_unique_ptr[`boost/openmethod/interop/std_unique_ptr.hpp`] to use
`std::unique_ptr` in virtual parameters.
Salesmen get a salary like employees, and on top get a commission on their
sales. The `next` function, available only in the body of an overrider, calls
the next most specialized overrider. It is similar to `super` in other
languages, except that it does not stand for an object, but for an overrider.
*The remaining headers are for advanced use*.
[source,c++]
----
include::{example}/1/salesman.cpp[tag=content]
----
## Pre-Core Headers
Paytime!
The following headers can be included before `core.hpp` to define custom
registries and policies, and override the default registry by defining
xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[`BOOST_OPENMETHOD_DEFAULT_REGISTRY`].
[source,c++]
----
include::{example}/1/main.cpp[tag=content]
----
### boost/openmethod/preamble.hpp
In the previous example, we used `next` to call the super-overrider. We can also
call an overrider directly. To do this, we can declare the overrider in the
header, and define it in an implementation file:
Defines `registry` and stock policy categories. Also defines all types and
functions necessary for the definition of `registry`.
[source,c++]
----
include::{example}/2/roles.hpp[tag=content]
----
### boost/openmethod/policies/std_rtti.hpp
Unlike function declarations,
xref:BOOST_OPENMETHOD_DECLARE_OVERRIDER.adoc[BOOST_OPENMETHOD_DECLARE_OVERRIDER]
cannot appear multiple times in a translation unit with the same arguments.
Also, it requires the _method_ itself to be defined prior using this macro.
Provides an implementation of the `rtti` policy using standard RTTI.
Overriders are placed in _overrider_ _containers_. An overrider container is a
class template named after the method, declared in the current namespace. It is
specialized for each overrider signature. Macro
xref:BOOST_OPENMETHOD_OVERRIDER.adoc[BOOST_OPENMETHOD_OVERRIDER] takes the same
arguments `BOOST_OPENMETHOD_OVERRIDE`, and expands to the corresponding
specialization of the overrider container. Containers have a static member
function `fn` that contains the body the overrider, provided by the user. We can
call the overrider for `Employee` like so:
### boost/openmethod/policies/fast_perfect_hash.hpp
[source,c++]
----
include::{example}/2/salesman.cpp[tag=content]
----
Provides an implementation of the `hash` policy using a fast perfect hash
function.
This is similar to a virtual function calling a base overrider. Virtual
functions don't have the equivalent of Smalltalk's or Python's `super`, but
OpenMethod does, it's `next`. It is almost always the right choice.
### boost/openmethod/policies/vptr_vector.hpp
The exception is: when performance is critical, we may want to inline the call
to the base overrider.
xref:BOOST_OPENMETHOD_INLINE_OVERRIDE.adoc[BOOST_OPENMETHOD_INLINE_OVERRIDE]
defines the overrider as an inline function, and it can go in a header file:
Provides an implementation of the `vptr` policy that stores the v-table pointers
in a `std::vector` indexed by type ids, possibly hashed.
[source,c++]
----
include::{example}/3/roles.hpp[tag=content]
----
### boost/openmethod/policies/default_error_handler.hpp
With inlining the overrider for `Salesman` compiles to (clang-20, x64-linux):
Provides an implementation of the `error_handler` policy that calls a
`std::function<void(openmethod_error)>` when an error is encountered, and before
the library aborts the program.
### boost/openmethod/policies/stderr_output.hpp
Provides an implementation of the `output` policy that writes diagnostics to
the C standard error stream (not using iostreams).
### boost/openmethod/default_registry.hpp
Defines the default registry, which contains all the stock policies listed
above. Includes all the headers listed in the preamble section so far.
### boost/openmethod/policies/static_rtti.hpp
Provides a minimal implementation of the `rtti` policy that does not depend on
standard RTTI.
### boost/openmethod/policies/throw_error_handler.hpp
Provides an implementation of the `error_handler` policy that throws errors as
exceptions.
### boost/openmethod/policies/vptr_map.hpp
Provides an implementation of the `vptr` policy that stores the v-table pointers
in a map (by default a `std::map`) indexed by type ids.
## High-level Headers
### boost/openmethod/core.hpp
Defines the main constructs of the library: methods, overriders and virtual
pointers, and mechanisms to implement them. Does not define any public macros
apart from `BOOST_OPENMETHOD_DEFAULT_REGISTRY`, if it is not defined already.
### boost/openmethod/macros.hpp
Defines the public macros of the library, such as `BOOST_OPENMETHOD`,
`BOOST_OPENMETHOD_CLASSES`, etc.
There is little point in including this header directly, as this has the same
effect as including `boost/openmethod.hpp`, which is shorter.
### boost/openmethod.hpp
Includes `core.hpp` and `macros.hpp`.
### boost/openmethod/interop/std_shared_ptr.hpp
Provides a `virtual_traits` specialization that make it possible to use a
`std::shared_ptr` in place of a raw pointer or reference in virtual parameters.
### boost/openmethod/interop/std_unique_ptr.hpp
Provides a `virtual_traits` specialization that make it possible to use a
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.
[source,asm]
----
movsd xmm0, qword ptr [rsi + 8]
mulsd xmm0, qword ptr [rip + .LCPI1_0]
addsd xmm0, qword ptr [rip + .LCPI1_1]
ret
----

View File

@@ -1,193 +0,0 @@
## Headers and Namespaces
Most real-life programs will be organized in multiple files and multiple
namespaces. OpenMethod interacts with headers and namespaces naturally, if
using-directives are avoided. In that case, there are a few things to be aware
of.
Let's break the Animals example into headers and namespaces. First we put
`Animal` in its own header and namespace:
[source,c++]
----
include::example$headers_namespaces/animal.hpp[]
----
`BOOST_OPENMETHOD` can be placed in a header file. It adds several constructs to
the current namespace:
* It declares (but does not define) a `struct` named after the method.
* It declares (but does not define) a _guide_ function. It is also named after
the method, and it has the same signature (with the `virtual_` decorators
stripped). It is used to match methods and overriders. It is never defined and
it is "called" only in a non-evaluated context.
* It defines an inline function with the same name and signature as the
method (with the `virtual_` decorators stripped).
Next, let's implement the `Cat` class, and a derived class, `Cheetah`, in the
`felines` namespace:
[source,c++]
----
include::example$headers_namespaces/cat.hpp[]
----
[source,c++]
----
include::example$headers_namespaces/cat.cpp[]
----
`BOOST_OPENMETHOD_CLASSES` should be placed in an implementation file. It can
also go in a header file, but this wastes space, as the same registrar will be
created in every translation unit that includes the header. It doesn't matter
which namespace the macro is called in. It can be used with any class name in
scope, or with qualified names.
`BOOST_OPENMETHOD_OVERRIDE` uses the guide function declared by
`BOOST_OPENMETHOD` to locate a method that can be called with the same arguments
as the overrider itself. It "calls" the guide function in a non-evaluated
context, passing it a `std::ostream&` and a `virtual_ptr<Cat>`. The return type
of the guide function is the method to add the overrider to. Exactly one guide
function must match. The normal rules of overload resolution apply. In that
case, the guide function is found via argument dependant lookup (ADL).
The macro adds several constructs to the current namespace:
* It declares (but does not define) a `struct` template with one type parameter,
named after the method. The template acts like a container for overriders.
* It specializes the template for the signature of the overrider. Inside the
struct, it defines the `next` and `has_next` members, and a static function
called `fn`. The block following the macro is the body of the `fn` function.
It follows that `BOOST_OPENMETHOD_OVERRIDE` should be placed in an
implementation file. `BOOST_OPENMETHOD_INLINE_OVERRIDE` works like
`BOOST_OPENMETHOD_OVERRIDE`, but it defines the `fn` function as inline, so it
can be used in a header file.
The overrider for Cats can be accessed in the same translation unit, after it
has been defined, using the `BOOST_OPENMETHOD_OVERRIDER` macro. It expands to
the specialization of the overrider container for the overrider's signature. We
call the static `fn` function to call the overrider.
NOTE: The Cheetah overrider calls the specific overrider for `Cat`, for
illustration purpose. It is usually better to call `next` instead.
Let's implement the `Dog` class, in the `canines` namespace. This time we want
the overrider to be accessible in other translation units. We can declare an
overrider with `BOOST_OPENMETHOD_DECLARE_OVERRIDER`, without actually defining
the static function `fn` just yet.
[source,c++]
----
include::example$headers_namespaces/dog.hpp[]
----
Unlike function declarations, which can occur multiple times in a TU, an
overrider declaration cannot. For example, this is illegal:
```c++
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
poke, (std::ostream&, virtual_ptr<Dog>), void);
BOOST_OPENMETHOD_DECLARE_OVERRIDER(
poke, (std::ostream&, virtual_ptr<Dog>), void);
```
Now we use `BOOST_OPENMETHOD_DEFINE_OVERRIDER` to define the overrider:
[source,c++]
----
include::example$headers_namespaces/dog.cpp[]
----
Let's look at the main program now. It derives `Bulldog` from `Dog` and provides
an overrider for the new class:
[source,c++]
----
include::example$headers_namespaces/main.cpp[]
----
Again ADL plays a role: it helps the overrider (and `main`) to locate the `poke`
method.
This example is the "happy scenario", where namespaces are used conservatively.
The `OVERRIDE` macros don't interact well with `using` directives. For example
this code:
```c++
using namespace animals;
using namespace canines;
using namespace felines;
struct Bulldog : Dog {
using Dog::Dog;
};
BOOST_OPENMETHOD_CLASSES(Dog, Bulldog);
BOOST_OPENMETHOD_OVERRIDE(
poke, (std::ostream & os, virtual_ptr<Bulldog> dog), void) {
next(os, dog);
os << " and bites back";
}
```
...will fail to compile, with an error like "reference to
'poke_boost_openmethod_overriders' is ambiguous". That is because the overrider
containers exist in both the canines and felines namespaces, with the same name.
Finally, the names passed as first arguments to the BOOST_OPENMETHOD and
BOOST_OPENMETHOD_OVERRIDE macros must be identifiers. Qualified names are not
allowed. Consider:
```c++
using animals::Animal;
namespace app_specific_behavior {
BOOST_OPENMETHOD(
meet, (std::ostream&, virtual_ptr<Animal>, virtual_ptr<Animal>), void);
} // namespace app_specific_behavior
BOOST_OPENMETHOD_OVERRIDE(
meet, (std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
os << "ignore";
}
```
Here, the guide function cannot be found, even via ADL. We get an error like
"use of undeclared identifier 'meet_boost_openmethod_guide'". How do we solve
this? We might be tempted to use a qualified name:
`app_specific_behavior::meet`:
```c++
BOOST_OPENMETHOD_OVERRIDE(
app_specific_behavior::meet,
(std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
os << "ignore";
}
```
But `BOOST_OPENMETHOD_OVERRIDE` also uses the name to derive the overrider
container's name, using preprocessor token pasting, resulting in an invalid
declaration error.
We need to do is to make `BOOST_OPENMETHOD_OVERRIDE` "see" the guide function.
Its name is returned by macro `BOOST_OPENMETHOD_GUIDE(NAME)`. We can use a
using-declaration to bring the guide function into the current scope:
```c++
using app_specific_behavior::BOOST_OPENMETHOD_GUIDE(meet);
BOOST_OPENMETHOD_OVERRIDE(
meet, (std::ostream& os, virtual_ptr<Animal>, virtual_ptr<Animal>), void) {
os << "ignore";
}
```

View File

@@ -64,7 +64,7 @@ Otherwise, there are serious problems with this approach.
* Programs that use the Node classes get the overriders of
`postfix`, even those that don't call `postfix` anywhere.
* They also pull in `postfix`'s transitive dependencies, in this case
* They also pull in `postfix`{empty}'s transitive dependencies, in this case
`std::ostream`, locales, facets, exceptions, etc. Note that now we need to
include `<iostream>` _before_ defining the classes.

View File

@@ -1,20 +1,30 @@
:example: ../examples/rolex
## Multiple Dispatch
A method can have more than one `virtual_ptr` parameter. For example:
A method can have more than one virtual parameter. This is often called
"multi-methods" or "multiple dispatch". All the virtual parameters participate
equally in overrider selection, following the same rules as those governing
overload resolution - except that the selection happens at runtime, and takes
into account the argument's dynamic types.
Multiple dispatch is occasionally useful, and, when it is needed, it can be
difficult to implement correctly and efficiently by hand. For example, given the following classes:
[source,c++]
----
include::example$hello_world.cpp[tag=multi]
include::{example}/7/main.cpp[tag=classes]
----
We want to implement an `approve` method that determines who can make what kind
of expenses. Employees can take any public transportation; managers can also
take a taxi, for a ride cost up to $100.00; and founders can take any
transportation, including a private jet. This can be expressed like so:
[source,c++]
----
include::example$hello_world.cpp[tag=multi_call,indent=0]
include::{example}/7/main.cpp[tag=approve]
----
The appropriate overrider is selected using a process similar to overload
resolution, with fallback options. If one overrider is more specialized than all
the others, call it. Otherwise, the return type is used as a tie-breaker, _if_
it is covariant with the return type of the base method. If there is still no
unique best overrider, one of the best overriders is chosen arbitrarily.
Note that `approve` takes advantage of inheritance to avoid listing all possible
cases explicitly. This is important, because the number of cases grows

View File

@@ -0,0 +1,49 @@
:example: ../examples/rolex
## Namespaces
Note;; This section uses overrider containers, described in the
xref:headers.adoc[Headers] section.
xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] defines a method in the current
namespace. xref:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE] works
_across_ namespaces. Overriders are not required to be in the same namespace as
the method they override. The macro adds the overrider a method that can be
called with the same arguments, possibly located via argument dependant lookup.
Overrider containers are added to the current namespace. It follows that the
same method can have overriders in several different container, in different
namespaces. This must be taken into account when calling an overrider
explicitly. Let's put Employee and Salesman in their own namespaces:
[source,c++]
----
include::{example}/4/roles.hpp[tag=content]
----
When we try to compile `salesman.cpp`:
[source,c++]
----
include::{example}/3/salesman.cpp[tag=content]
----
We get an error like:
```
error: implicit instantiation of undefined template
'sales::pay_boost_openmethod_overriders<
double (boost::openmethod::virtual_ptr<const employees::Employee>)>
```
This is because the overrider container for `pay` in namespace `sales` is
specialized for `Salesman`, but not for `Employee`. The overrider for Employee
is in a specialization of a different container, in namespace `employees`.
The solution is to qualify the overrider container with the `employees`
namespace:
[source,c++]
----
include::{example}/3/salesman.cpp[tag=content]
----

View File

@@ -5,7 +5,7 @@ Open-methods can be as fast as ordinary virtual member functions when
compiled with optimization.
First, let's examine the code generated by clang for an ordinary virtual
function call.
function call:
[source,c++]
----
@@ -38,19 +38,19 @@ This compiles to (variable names are shortened for readability):
[source,asm]
----
mov rax, rdi
mov rcx, qword ptr [rdi]
mov rdi, qword ptr [rip + mult]
imul rdi, qword ptr [rcx - 8]
movzx ecx, byte ptr [rip + shift]
shr rdi, cl
mov rdx, rsi
mov rcx, qword ptr [rip + vptr_vector_vptrs]
mov rdi, qword ptr [rcx + 8*rdi]
mov rcx, qword ptr [rip + fn+88]
mov rcx, qword ptr [rdi + 8*rcx]
mov rsi, rax
jmp rcx # TAILCALL
mov rax, rdi
mov rcx, qword ptr [rdi]
mov rdi, qword ptr [rip + mult]
imul rdi, qword ptr [rcx - 8]
movzx ecx, byte ptr [rip + shift]
shr rdi, cl
mov rdx, rsi
mov rcx, qword ptr [rip + vptr_vector_vptrs]
mov rdi, qword ptr [rcx + 8*rdi]
mov rcx, qword ptr [rip + fn+88]
mov rcx, qword ptr [rdi + 8*rcx]
mov rsi, rax
jmp rcx # TAILCALL
----
This is quite a few instructions more. Upon closer examination, we see that many
@@ -62,9 +62,9 @@ llvm-mca estimates a throughput of 4 cycles per dispatch. However, the
difference is amortized by the time spent passing the arguments and returning
from the function; plus, of course, executing the body of the function.
Micro and RDTSC-based benchmarks suggest that dispatching an open-methods with a
single virtual argument _via_ _a_ _reference_ is between 30% and 50% slower than
calling the equivalent virtual function, with an empty body and no other
Micro- and RDTSC-based benchmarks suggest that dispatching an open-methods with
a single virtual argument _via_ _a_ _reference_ is between 30% and 50% slower
than calling the equivalent virtual function, with an empty body and no other
arguments. In most real programs, the overhead would be unnoticeable.
*However*, `call_via_ref` does two things: it constructs a `virtual_ptr<Node>`
@@ -94,10 +94,10 @@ mov rax, qword ptr [rdi + 8*rax]
jmp rax # TAILCALL
----
`virtual_ptr` arguments as passed through the method call, to the overrider,
`virtual_ptr` arguments are passed through the method call, to the overrider,
which can use them to make further method calls.
A program designed with open-methods in mind should store `virtual_ptr`{empty}'s
A program designed with open-methods in mind should use `virtual_ptr`{empty}'s
in place of plain pointers or references, as much as possible. Here is the Node
example, rewritten to use `virtual_ptr`{empty}'s thoughout:

View File

@@ -0,0 +1,111 @@
[#ref_headers]
= xref:ref_headers.adoc[Headers]
{empty}
## Headers for General Use
The following headers are sufficient for most basic uses of the library.
* xref:#main[`boost/openmethod.hpp`] to define open-methods and overriders using
convenient macros.
* xref:#initialize[`boost/openmethod/initialize.hpp`] to initialize the library.
Typically only included in the translation unit containing `main`.
The following headers make it possible to use standard smart pointers in virtual
parameters:
* xref:#std_shared_ptr[`boost/openmethod/interop/std_shared_ptr.hpp`] to use
`std::shared_ptr` in virtual parameters.
* xref:#std_unique_ptr[`boost/openmethod/interop/std_unique_ptr.hpp`] to use
`std::unique_ptr` in virtual parameters.
*The remaining headers are for advanced use*.
## Pre-Core Headers
The following headers can be included before `core.hpp` to define custom
registries and policies, and override the default registry by defining
xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[`BOOST_OPENMETHOD_DEFAULT_REGISTRY`].
### boost/openmethod/preamble.hpp
Defines `registry` and stock policy categories. Also defines all types and
functions necessary for the definition of `registry`.
### boost/openmethod/policies/std_rtti.hpp
Provides an implementation of the `rtti` policy using standard RTTI.
### boost/openmethod/policies/fast_perfect_hash.hpp
Provides an implementation of the `hash` policy using a fast perfect hash
function.
### boost/openmethod/policies/vptr_vector.hpp
Provides an implementation of the `vptr` policy that stores the v-table pointers
in a `std::vector` indexed by type ids, possibly hashed.
### boost/openmethod/policies/default_error_handler.hpp
Provides an implementation of the `error_handler` policy that calls a
`std::function<void(openmethod_error)>` when an error is encountered, and before
the library aborts the program.
### boost/openmethod/policies/stderr_output.hpp
Provides an implementation of the `output` policy that writes diagnostics to
the C standard error stream (not using iostreams).
### boost/openmethod/default_registry.hpp
Defines the default registry, which contains all the stock policies listed
above. Includes all the headers listed in the preamble section so far.
### boost/openmethod/policies/static_rtti.hpp
Provides a minimal implementation of the `rtti` policy that does not depend on
standard RTTI.
### boost/openmethod/policies/throw_error_handler.hpp
Provides an implementation of the `error_handler` policy that throws errors as
exceptions.
### boost/openmethod/policies/vptr_map.hpp
Provides an implementation of the `vptr` policy that stores the v-table pointers
in a map (by default a `std::map`) indexed by type ids.
## High-level Headers
### boost/openmethod/core.hpp
Defines the main constructs of the library: methods, overriders and virtual
pointers, and mechanisms to implement them. Does not define any public macros
apart from `BOOST_OPENMETHOD_DEFAULT_REGISTRY`, if it is not defined already.
### boost/openmethod/macros.hpp
Defines the public macros of the library, such as `BOOST_OPENMETHOD`,
`BOOST_OPENMETHOD_CLASSES`, etc.
There is little point in including this header directly, as this has the same
effect as including `boost/openmethod.hpp`, which is shorter.
### boost/openmethod.hpp
Includes `core.hpp` and `macros.hpp`.
### boost/openmethod/interop/std_shared_ptr.hpp
Provides a `virtual_traits` specialization that make it possible to use a
`std::shared_ptr` in place of a raw pointer or reference in virtual parameters.
### boost/openmethod/interop/std_unique_ptr.hpp
Provides a `virtual_traits` specialization that make it possible to use a
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.

View File

@@ -22,15 +22,35 @@ counterparts - e.g. from `virtual_ptr<std::unique_ptr<const Node>>` to
`virtual_ptr<const Node>`. Methods and overriders typically use plain
`virtual_ptr`{empty}'s, although it is not always the case. For example,
consider a `transpose` method for matrices. If the matrix is symmetric, the
overrider should return its argument. This can be implemented by passing a `virtual_ptr<std::shared_ptr<const Matrix>>` to the method.
overrider should return its argument. This can be implemented by passing a
`virtual_ptr<std::shared_ptr<const Matrix>>` to the method.
The reverse conversion, from plain to smart, does not exist, because it would be
The reverse conversion, from plain to smart, does not exist, because it is
unsafe.
A smart `virtual_ptr` can be constructed from a corresponding smart pointer, but
not directly from a plain reference or pointer, because it has the potential to
accidentally create smart pointers.
The library provides aliases for standard smart pointers:
- `unique_virtual_ptr<Class>` is an alias for `virtual_ptr<std::unique_ptr<Class>>`
- `shared_virtual_ptr<Class>` is an alias for `virtual_ptr<std::shared_ptr<Class>>`
The standard library provides `std::make_unique` and `std::make_shared` to
create smart pointers. They are convenient, robust, and, in the case of
`std::shared_ptr`, more efficient. OpenMethod provides counterparts:
- `make_unique_virtual_ptr<Class>(...)`
- `make_shared_virtual_ptr<Class>(...)`
Since these functions create the object, they know its exact type with
certainty. Thus they don't need to perform a hash table lookup to find the
appropriate v-table; they simply read it from a static variable. As a
consequence, they don't even require `Class` to be polymorphic.
Here is a variation of the AST example that uses dynamic allocation and unique
pointers:

View File

@@ -1813,7 +1813,8 @@ auto operator!=(
template<class Class, class Registry>
struct virtual_traits<virtual_ptr<Class, Registry>, Registry> {
//! `Class`, stripped from cv-qualifiers.
using virtual_type = typename virtual_ptr<Class, Registry>::element_type;
using virtual_type =
std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;
//! Return a reference to a non-modifiable `Class` object.
//! @param arg A reference to a non-modifiable `Class` object.
@@ -1858,7 +1859,8 @@ struct virtual_traits<virtual_ptr<Class, Registry>, Registry> {
template<class Class, class Registry>
struct virtual_traits<const virtual_ptr<Class, Registry>&, Registry> {
//! `Class`, stripped from cv-qualifiers.
using virtual_type = typename virtual_ptr<Class, Registry>::element_type;
using virtual_type =
std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;
//! Return a reference to a non-modifiable `Class` object.
//! @param arg A reference to a non-modifiable `Class` object.
@@ -2317,8 +2319,7 @@ class method<Id, ReturnType(Parameters...), Registry>
(void)&impl;
}
static override_impl<Function, FnReturnType>
impl;
static override_impl<Function, FnReturnType> impl;
};
};
@@ -2628,21 +2629,45 @@ template<class T, class R>
struct validate_overrider_parameter<virtual_ptr<T, R>, virtual_ptr<T, R>, void>
: std::true_type {};
template<class T1, class R1, class T2, class R2>
template<class T1, class R, class T2, class R2>
struct validate_overrider_parameter<
virtual_ptr<T1, R1>, virtual_ptr<T2, R2>, void> : std::is_same<R1, R2> {
static_assert(validate_overrider_parameter::value, "registry mismatch");
virtual_ptr<T1, R>, virtual_ptr<T2, R2>, void> : std::true_type {
static_assert(std::is_same_v<R, R2>, "registry mismatch");
using C1 = virtual_type<virtual_ptr<T1, R>, R>;
using C2 = virtual_type<virtual_ptr<T2, R>, R>;
static_assert(
std::is_base_of_v<C1, C2> &&
std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
"method parameter must be an unambiguous accessible base "
"of corresponding overrider parameter");
};
template<class T1, class R1, class T2, class R2>
template<class T1, class R, class T2, class R2>
struct validate_overrider_parameter<
const virtual_ptr<T1, R1>&, const virtual_ptr<T2, R2>&, void>
: validate_overrider_parameter<virtual_ptr<T1, R1>, virtual_ptr<T2, R2>> {};
const virtual_ptr<T1, R>&, const virtual_ptr<T2, R2>&, void>
: std::true_type {
static_assert(std::is_same_v<R, R2>, "registry mismatch");
using C1 = virtual_type<const virtual_ptr<T1, R>&, R>;
using C2 = virtual_type<const virtual_ptr<T2, R>&, R>;
static_assert(
std::is_base_of_v<C1, C2> &&
std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
"method parameter must be an unambiguous accessible base "
"of corresponding overrider parameter");
};
template<class T1, class R1, class T2, class R2>
template<class T1, class R, class T2, class R2>
struct validate_overrider_parameter<
virtual_ptr<T1, R1>&&, virtual_ptr<T2, R2>&&, void>
: validate_overrider_parameter<virtual_ptr<T1, R1>, virtual_ptr<T2, R2>> {};
virtual_ptr<T1, R>&&, virtual_ptr<T2, R2>&&, void> : std::true_type {
static_assert(std::is_same_v<R, R2>, "registry mismatch");
using C1 = virtual_type<virtual_ptr<T1, R>&&, R>;
using C2 = virtual_type<virtual_ptr<T2, R>&&, R>;
static_assert(
std::is_base_of_v<C1, C2> &&
std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
"method parameter must be an unambiguous accessible base "
"of corresponding overrider parameter");
};
} // namespace detail

View File

@@ -62,6 +62,8 @@ struct va_args<ReturnType> {
::boost::openmethod::detail::va_args<__VA_ARGS__>::registry>
#define BOOST_OPENMETHOD(NAME, ARGS, ...) \
template<typename...> \
struct BOOST_OPENMETHOD_OVERRIDERS(NAME); \
struct BOOST_OPENMETHOD_ID(NAME); \
template<typename... ForwarderParameters> \
typename ::boost::openmethod::detail::enable_forwarder< \
@@ -70,8 +72,8 @@ struct va_args<ReturnType> {
ForwarderParameters...>::type \
BOOST_OPENMETHOD_GUIDE(NAME)(ForwarderParameters && ... args); \
template<typename... ForwarderParameters> \
inline auto NAME(ForwarderParameters&&... args) \
->typename ::boost::openmethod::detail::enable_forwarder< \
inline auto NAME(ForwarderParameters&&... args) -> \
typename ::boost::openmethod::detail::enable_forwarder< \
void, BOOST_OPENMETHOD_TYPE(NAME, ARGS, __VA_ARGS__), \
::boost::openmethod::detail::va_args<__VA_ARGS__>::return_type, \
ForwarderParameters...>::type { \
@@ -89,27 +91,24 @@ struct va_args<ReturnType> {
};
#define BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, ...) \
template<typename> \
template<typename...> \
struct BOOST_OPENMETHOD_OVERRIDERS(NAME); \
template<> \
struct BOOST_OPENMETHOD_OVERRIDERS(NAME)<__VA_ARGS__ ARGS> { \
BOOST_OPENMETHOD_DETAIL_LOCATE_METHOD(NAME, ARGS); \
static auto fn ARGS -> __VA_ARGS__; \
static auto fn ARGS->__VA_ARGS__; \
static auto has_next() -> bool; \
template<typename... Args> \
static auto next(Args&&... args) -> decltype(auto); \
}; \
inline auto BOOST_OPENMETHOD_OVERRIDERS( \
NAME)<__VA_ARGS__ ARGS>::has_next() \
->bool { \
NAME)<__VA_ARGS__ ARGS>::has_next() -> bool { \
return boost_openmethod_detail_locate_method_aux< \
void ARGS>::type::has_next<fn>(); \
} \
template<typename... Args> \
inline auto BOOST_OPENMETHOD_OVERRIDERS(NAME)<__VA_ARGS__ ARGS>::next( \
Args&&... args) \
->decltype(auto) { \
BOOST_ASSERT(has_next()); \
Args&&... args) -> decltype(auto) { \
return boost_openmethod_detail_locate_method_aux< \
void ARGS>::type::next<fn>(std::forward<Args>(args)...); \
}
@@ -124,7 +123,7 @@ struct va_args<ReturnType> {
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(NAME, ARGS, ...) \
BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
auto BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, __VA_ARGS__)::fn ARGS \
->boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
-> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
#define BOOST_OPENMETHOD_OVERRIDE(NAME, ARGS, ...) \
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
@@ -134,7 +133,7 @@ struct va_args<ReturnType> {
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
inline auto BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, __VA_ARGS__)::fn ARGS \
->boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
-> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>
#define BOOST_OPENMETHOD_CLASSES(...) \
BOOST_OPENMETHOD_REGISTER(::boost::openmethod::use_classes<__VA_ARGS__>);