Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

// clang-format off

#include <cstdint>
#include <limits>

// tag::classes[]
struct Node {
virtual ~Node() {}
Expand Down Expand Up @@ -56,12 +59,22 @@ struct custom_rtti : boost::openmethod::policies::rtti {

using type_id = boost::openmethod::type_id; // for brevity

template<typename T>
inline static std::uintptr_t np_id = 0;

inline static std::uintptr_t np_id_alloc =
std::numeric_limits<std::uintptr_t>::max();

template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return reinterpret_cast<type_id>(T::static_type);
} else {
return reinterpret_cast<type_id>(0);
if (np_id<T> == 0) {
np_id<T> = np_id_alloc--;
}

return reinterpret_cast<type_id>(np_id<T>);
}
}

Expand Down
15 changes: 14 additions & 1 deletion doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

// clang-format off

#include <cstdint>
#include <limits>

// tag::classes[]
struct Node {
Node(unsigned type) : type(type) {}
Expand Down Expand Up @@ -52,12 +55,22 @@ struct custom_rtti : boost::openmethod::policies::deferred_static_rtti {

using type_id = boost::openmethod::type_id; // for brevity

template<typename T>
inline static std::uintptr_t np_id = 0;

inline static std::uintptr_t np_id_alloc =
std::numeric_limits<std::uintptr_t>::max();

template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return reinterpret_cast<type_id>(T::static_type);
} else {
return reinterpret_cast<type_id>(0);
if (np_id<T> == 0) {
np_id<T> = np_id_alloc--;
}

return reinterpret_cast<type_id>(np_id<T>);
}
}

Expand Down
116 changes: 116 additions & 0 deletions doc/modules/ROOT/examples/rolex/8/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) 2017-2026 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>

struct Animal {
virtual ~Animal() = default;
};

struct Dog : Animal {};
struct Cat : Animal {};

BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat);

// tag::zoo[]
struct Zoo {
BOOST_OPENMETHOD_MEM(
poke, (boost::openmethod::virtual_ptr<Animal>), std::string);
};
// end::zoo[]

// tag::zookeeper[]
class ZooKeeper {
BOOST_OPENMETHOD_OVERRIDE_MEM(
Zoo::poke, (boost::openmethod::virtual_ptr<Dog>), std::string) {
return "bark";
}
BOOST_OPENMETHOD_OVERRIDE_MEM(
Zoo::poke, (boost::openmethod::virtual_ptr<Cat>), std::string) {
return "hiss";
}
};
// end::zookeeper[]

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;
}

BOOST_OPENMETHOD_OVERRIDE_MEM(
pay,
(Payroll & payroll, boost::openmethod::virtual_ptr<const Employee>),
double) {
double amount = 5000.0;
payroll.update_balance(-amount);
return amount;
}

BOOST_OPENMETHOD_OVERRIDE_MEM(
pay,
(Payroll & payroll, boost::openmethod::virtual_ptr<const Salesman> emp),
double) {
using self = BOOST_OPENMETHOD_OVERRIDER_MEM(
Payroll, pay,
(Payroll&, boost::openmethod::virtual_ptr<const Salesman>), double);
double base = self::method_type::next<self::fn>(payroll, emp);
double commission = emp->sales * 0.05;
payroll.update_balance(-commission);
return base + commission;
}
};
// end::payroll[]

// ...and let's not forget to register the classes
BOOST_OPENMETHOD_CLASSES(Employee, Salesman);

// tag::main[]
int main() {
boost::openmethod::initialize();

Dog snoopy;
Cat felix;
std::cout << "poke dog: " << Zoo::poke(snoopy) << "\n"; // bark
std::cout << "poke cat: " << Zoo::poke(felix) << "\n"; // hiss

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[]
2 changes: 1 addition & 1 deletion doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
** xref:smart_pointers.adoc[Smart Pointers]
** xref:headers.adoc[Header and Implementation Files]
** xref:namespaces.adoc[Namespaces]
** xref:friends.adoc[Friends]
** xref:privacy.adoc[Members and Friends]
** xref:multiple_dispatch.adoc[Multiple Dispatch]
* Advanced Features
** xref:core_api.adoc[Core API]
Expand Down
24 changes: 22 additions & 2 deletions doc/modules/ROOT/pages/custom_rtti.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ struct std_rtti : rtti {
`virtual_ptr`{empty}s "final" constructs.
It is also called to set `bad_call::method`.
This function is required.
+
It is asked for the type id of types that are not registered classes, and of
types that are not classes at all: non-virtual parameter types, function types,
and two of the library's own - a method, and the registrar that stands for an
overrider. Those ids are keys, not labels. `initialize` groups the copies of a
method that several modules each registered by the type id of the method, and
the copies of an overrider by the type id of its registrar; a policy that
answers with one shared value for every type it does not recognise makes
distinct methods, or distinct overriders, indistinguishable, and they are
silently merged. So `static_type` must return a different value for every
different type - see the example below. Across modules it must return the same
value for a given type in each of them, up to `type_index`. `dynamic_type` is
under no such obligation: it is only ever called on an instance of a registered
polymorphic class.

* `dynamic_type` is used to read the dynamic type of a virtual argument.
If only the `virtual_ptr` "final" constructs are used, or if
Expand Down Expand Up @@ -116,12 +130,18 @@ not require Node to be polymorphic in the C++ sense. If we remove the virtual
destructor and implement `value` as an open-method as well, the program will
still work.

`static_type` has to answer for the types that are not `Node`{empty}s too, and
its answers have to be distinct, so it hands each one an id of its own on first
use, counting down from the largest value a `type_id` can hold. Counting down
keeps them clear of the small ids the `Node` hierarchy assigns itself counting
up. Returning a single value such as zero for all of them would compile and run,
and would quietly merge two methods, or two overriders of one method, into one.

The policy is quite minimal. It does not support virtual inheritance, because it
does not provide a `dynamic_cast_ref` function. It would not produce good error
or trace messages, because it does not provide a `type_name` function. Instead,
it relies on the `type_name` inherited from cpp:rtti::defaults[]. It
renders types as adorned integers, e.g. "type_id(2)". All non-"polymorphic"
types would be rendered the same way, as "type_id(0)".
renders types as adorned integers, e.g. "type_id(2)".

cpp:rtti::defaults[] also provides a default implementation for `type_index`,
which simply returns its argument.
Expand Down
49 changes: 0 additions & 49 deletions doc/modules/ROOT/pages/friends.adoc

This file was deleted.

110 changes: 110 additions & 0 deletions doc/modules/ROOT/pages/privacy.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
:page-aliases: friends.adoc
:example: ../examples/rolex

[#members]

A method may itself be a `static` member function of a class, declared with
xref:reference:BOOST_OPENMETHOD_MEM.adoc[BOOST_OPENMETHOD_MEM] instead of
xref:reference:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD]:

[source,c++]
----
include::{example}/8/main.cpp[tag=zoo]
----

`Zoo::poke` is called as `Zoo::poke(animal)`. There is no implicit object
parameter and no dispatch on `this` - dispatch still goes entirely by the
method's own virtual parameters, exactly as for a free method. `poke` may be
overloaded within `Zoo`, just as a free method may be overloaded at namespace
scope.

An overrider, too, may be a `static` member function - of any class, not
necessarily the one the method belongs to - added with
xref:reference:BOOST_OPENMETHOD_OVERRIDE_MEM.adoc[BOOST_OPENMETHOD_OVERRIDE_MEM]
instead of xref:reference:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE]:

[source,c++]
----
include::{example}/8/main.cpp[tag=zookeeper]
----

The first argument names the method being overridden - here `Zoo::poke` - and
may equally name a free method declared with
xref:reference:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD].

Since a member overrider's class need not be the method's own, this is what
lets an overrider reach a class's private state with no `friend` declaration
at all: being a member is already enough access. Let's revisit the `pay`
example once more, updating a `balance` in a `Payroll` class:

[source,c++]
----
include::{example}/8/main.cpp[tag=pay]
----

[source,c++]
----
include::{example}/8/main.cpp[tag=payroll]
----

`update_balance` is `private`, and both overriders are members of `Payroll`,
so they call it as an ordinary same-class private call - no `friend` in
sight. `next`/`has_next` are not available by name inside a `_MEM` overrider's
body the way they are in a free one's; the second overrider reaches the first
through the core API instead, via
xref:reference:BOOST_OPENMETHOD_OVERRIDER_MEM.adoc[BOOST_OPENMETHOD_OVERRIDER_MEM],
passing it the class, method name, parameter list and return type of the
overrider doing the call - naming *itself*, not the overrider it calls.

This does not apply when the class the overrider needs access to is not one
the caller controls - a third-party type with no room to add a member. The
rest of this page covers `friend`, which remains the way to grant access in
that case.

[#friendship]

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 leaves the choice of using
`friend`, or not, to the user.

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++]
----
include::{example}/5/main.cpp[tag=pay]
----

xref:reference:BOOST_OPENMETHOD.adoc[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}/5/main.cpp[tag=payroll]
----

We can now implement the `pay` overriders:

[source,c++]
----
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.
Loading
Loading