mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 04:22:12 +00:00
small doc improvements
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
|
||||
## inplace_vptr
|
||||
|
||||
### Synopsis
|
||||
|
||||
Defined in <boost/openmethod/inplace_vptr.hpp>.
|
||||
|
||||
```c++
|
||||
namespace boost::openmethod {
|
||||
|
||||
template<class Class, class Policy = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
|
||||
class inplace_vptr {
|
||||
protected:
|
||||
inplace_vptr();
|
||||
~inplace_vptr();
|
||||
friend auto boost_openmethod_vptr(const Class& obj) -> vptr_type;
|
||||
};
|
||||
|
||||
template<class Class, class Base, class... MoreBases>
|
||||
class inplace_vptr {
|
||||
protected:
|
||||
inplace_vptr();
|
||||
~inplace_vptr();
|
||||
friend auto boost_openmethod_vptr(const Class& obj) -> vptr_type;
|
||||
// if sizeof(MoreBases...) > 0
|
||||
};
|
||||
|
||||
} // namespace boost::openmethod
|
||||
```
|
||||
|
||||
### Description
|
||||
|
||||
`inplace_vptr` is a CRTP class template that embeds and manages a vptr across a
|
||||
class hierarchy.
|
||||
|
||||
If `Class` has no `Bases`, `inplace_vptr` adds a `boost_openmethod_vptr` private
|
||||
member to `Class`. In either case, it sets the vptr to the v-table of `Class`
|
||||
from `Policy`. It also creates a `boost_openmethod_vptr` friend function that
|
||||
takes a a `const Class&` and returns the embedded vptr.
|
||||
|
||||
If `Class` has more than one base, the `boost_openmethod_vptr` friend
|
||||
function is also created. It returns one of the embedded vptrs (it doesn't
|
||||
matter which one, as they all have the same value). This is to resolve
|
||||
ambiguities
|
||||
|
||||
As part of its implementation, `inplace_vptr` may also declare one or two free
|
||||
functions (`boost_openmethod_policy` and `boost_openmethod_bases`) at certain
|
||||
levels of the hierarchy.
|
||||
|
||||
### Members
|
||||
|
||||
#### constructor
|
||||
|
||||
```c++
|
||||
inplace_vptr();
|
||||
```
|
||||
|
||||
Sets the vptr to the v-table for Class, obtained from `Policy`. If `Policy`
|
||||
contains `indirect_vptr`, an additional level of indirection is added, thus
|
||||
preserving the validity of the pointer across calls to `initialize`.
|
||||
|
||||
|
||||
#### destructor
|
||||
|
||||
```c++
|
||||
~inplace_vptr();
|
||||
```
|
||||
|
||||
For each `Base`, sets the vptr to the v-table for that base.
|
||||
|
||||
#### Free Functions
|
||||
|
||||
```c++
|
||||
auto boost_openmethod_vptr(const Class& obj) -> vptr_type;
|
||||
```
|
||||
|
||||
Returns the vptr embedded in `obj`.
|
||||
@@ -4,6 +4,8 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// employee.cpp
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// tag::content[]
|
||||
// salesman.cpp
|
||||
|
||||
#include "roles.hpp"
|
||||
#include <boost/openmethod.hpp>
|
||||
|
||||
|
||||
@@ -22,6 +22,5 @@ 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[]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
* xref:introduction.adoc[Introduction]
|
||||
* xref:basics.adoc[Basics]
|
||||
* xref:basics.adoc[Open-Methods 101]
|
||||
* xref:performance.adoc[Performance]
|
||||
* xref:smart_pointers.adoc[Smart Pointers]
|
||||
* xref:headers.adoc[Headers]
|
||||
* xref:namespaces.adoc[Namespaces]
|
||||
* xref:friendship.adoc[Friendship]
|
||||
* xref:multiple_dispatch.adoc[Multiple Dispatch]
|
||||
* Advanced Features
|
||||
* xref:advanced_features.adoc[Advanced Features]
|
||||
** xref:core_api.adoc[Core API]
|
||||
** xref:registries_and_policies.adoc[Registries and Policies]
|
||||
** xref:custom_rtti.adoc[Custom RTTI]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:exampledir: ../example
|
||||
|
||||
## Basics
|
||||
## Open-Methods 101
|
||||
|
||||
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 from a
|
||||
@@ -11,7 +11,8 @@ with one virtual parameter is equivalent to a virtual function - with one big
|
||||
difference: it exists outside of classes.
|
||||
|
||||
A virtual parameter is in the form `virtual_ptr<Class>`. It is a pointer-like
|
||||
class that points to an instance of `Class`.
|
||||
class that points to an instance of `Class`. `virtual_ptr` is defined in the library's main namespace,
|
||||
`boost::openmethod`.
|
||||
|
||||
To create an open-method that implements the `postfix` operation, we use the
|
||||
xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] macro:
|
||||
|
||||
@@ -50,7 +50,7 @@ struct std_rtti : rtti {
|
||||
This template is required.
|
||||
|
||||
* `static_type` is called during class registration, by
|
||||
`virtual_ptr`{empty}'s "final" constructs.
|
||||
`virtual_ptr`{empty}s "final" constructs.
|
||||
It is also called to set `bad_call::method`.
|
||||
This function is required.
|
||||
|
||||
|
||||
@@ -2,30 +2,27 @@
|
||||
|
||||
## Headers
|
||||
|
||||
Typically, `BOOST_OPENMETHOD` go in headers, while
|
||||
`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` go in
|
||||
Typically, `BOOST_OPENMETHOD` is used in headers, while
|
||||
`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` are used in
|
||||
implementation files.
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/1/roles.hpp[tag=content]
|
||||
----
|
||||
|
||||
`BOOST_OPENMETHOD` _defines_ an inline function, so it can be called only
|
||||
once in a translation unit.
|
||||
The include guards see to this.
|
||||
`BOOST_OPENMETHOD` _defines_ an inline function, so it can be called only once
|
||||
in a translation unit. The include guards see to this.
|
||||
|
||||
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:
|
||||
`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:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
|
||||
@@ -8,8 +8,9 @@ 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.
|
||||
the method they override. The macro adds the overrider to a method that can be
|
||||
called with the same arguments as the overrider, 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
|
||||
@@ -45,5 +46,5 @@ namespace:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/3/salesman.cpp[tag=content]
|
||||
include::{example}/4/salesman.cpp[tag=content]
|
||||
----
|
||||
|
||||
@@ -47,16 +47,15 @@ This compiles to (variable names are shortened for readability):
|
||||
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 [rip + postfix::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
|
||||
are memory reads, independent of one another; they can be thus executed in
|
||||
parallel. For example, the first three instructions would execute simultaneously
|
||||
on a modern CPU.
|
||||
are memory reads, independent of one another; they can thus be executed in
|
||||
parallel. For example, the first three instructions can execute simultaneously.
|
||||
|
||||
llvm-mca estimates a throughput of 4 cycles per dispatch. However, the
|
||||
difference is amortized by the time spent passing the arguments and returning
|
||||
@@ -97,9 +96,9 @@ jmp rax # TAILCALL
|
||||
`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 use `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:
|
||||
example, rewritten to use `virtual_ptr`{empty}s thoughout:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
|
||||
@@ -22,7 +22,7 @@ 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*.
|
||||
*The headers below are for advanced use*.
|
||||
|
||||
## Pre-Core Headers
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ across modules.
|
||||
### Indirect Vptrs
|
||||
|
||||
`initialize` rebuilds the v-tables in the registry. This invalidates all the
|
||||
`virtual_ptr`{empty}'s, and also the v-table pointers stored in objects by
|
||||
`virtual_ptr`{empty}s, and also the v-table pointers stored in objects by
|
||||
cpp:inplace_vptr_base[], related to that registry. This is seldom an issue, as
|
||||
most programs that dynamically load shared libraries do so at the very beginning
|
||||
of their execution.
|
||||
@@ -125,7 +125,7 @@ that has the same policies as `default_registry`, plus `indirect_vptr`. We can
|
||||
use it to override the default registry, for example using a compiler
|
||||
command-line switch (`-DBOOST_OPENMETHOD_DEFAULT_REGISTRY=indirect_registry`).
|
||||
|
||||
Here is an example of a program that carries `virtual_ptr`{empty}'s across
|
||||
Here is an example of a program that carries `virtual_ptr`{empty}s across
|
||||
`initialize` calls:
|
||||
|
||||
[source,c++]
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
## Smart Pointers
|
||||
|
||||
If we want maximum performance, we want to use `virtual_ptr`{empty}s in place of
|
||||
ordinary pointers or references. However, many designs want to use smart
|
||||
pointers for lifetime management; think of `std::unique_ptr`, `std::shared_ptr`,
|
||||
or `boost::intrusive_ptr`.
|
||||
ordinary pointers or references. However, we may also want to use smart pointers
|
||||
for lifetime management; think of `std::unique_ptr`, `std::shared_ptr`, or
|
||||
`boost::intrusive_ptr`.
|
||||
|
||||
Does it mean that we have to choose between the performance of `virtual_ptr` and
|
||||
the convenience of `std::unique_ptr`? Or carry around both types of smart
|
||||
@@ -17,20 +17,18 @@ Node>>` both point to a `const Node`; the former uses a plain `const Node*`
|
||||
while the latter uses a `std::unique_ptr<const Node>`. Both carry the same
|
||||
v-table pointer.
|
||||
|
||||
Smart `virtual_ptr`{empty}'s automatically convert to their non-smart, "plain"
|
||||
Smart `virtual_ptr`{empty}s automatically convert to their non-smart, "plain"
|
||||
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,
|
||||
`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.
|
||||
|
||||
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 reverse conversion, from plain to smart, does not exist, because it would
|
||||
have the potential to accidentally create smart pointers. Likewise, a smart
|
||||
`virtual_ptr` can be constructed from a smart pointer, but not directly from a
|
||||
plain reference or pointer.
|
||||
|
||||
The library provides aliases for standard smart pointers:
|
||||
|
||||
@@ -39,8 +37,9 @@ The library provides aliases for standard smart pointers:
|
||||
- `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:
|
||||
create smart pointers. They are convenient, robust in presence of exceptions,
|
||||
and, in the case of `std::shared_ptr`, more efficient. OpenMethod provides
|
||||
these counterparts:
|
||||
|
||||
- `make_unique_virtual_ptr<Class>(...)`
|
||||
|
||||
@@ -49,7 +48,16 @@ create smart pointers. They are convenient, robust, and, in the case of
|
||||
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.
|
||||
consequence, they don't require `Class` to be polymorphic.
|
||||
|
||||
The aliases and the `make_*` functions are aliased in `namespace
|
||||
boost::openmethod::aliases`, making it convenient to import constructs that are
|
||||
likely to be used together.
|
||||
|
||||
Smart `virtual_ptr`{empty}s are implemented in their own headers, found in the
|
||||
`interop` subdirectory. For example, support for `std::unique_ptr` is provided
|
||||
in `<boost/openmethod/interop/std_unique_ptr.hpp>`. `<boost/openmethod.hpp>`
|
||||
does not include smart pointer headers, so they must be included explicitly.
|
||||
|
||||
Here is a variation of the AST example that uses dynamic allocation and unique
|
||||
pointers:
|
||||
|
||||
@@ -70,9 +70,9 @@ jmp qword ptr [rax + 8*rcx] # TAILCALL
|
||||
### `inplace_vptr`
|
||||
|
||||
cpp:inplace_vptr_base[] and cpp:inplace_vptr_derived[] automate the creation and
|
||||
management of embedded vptrs. They are both are
|
||||
management of embedded vptrs. They are both
|
||||
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern[CRTP]
|
||||
classes.
|
||||
mixin classes.
|
||||
|
||||
`inplace_vptr_base` is used at the root of a hierarchy. It defines a vptr member
|
||||
variable, and a `boost_openmethod_vptr` friend function that returns its value -
|
||||
@@ -80,6 +80,10 @@ just like in the previous example. `inplace_vptr_derived` is used in derived
|
||||
classes. Their constructors set the vptr member to point to v-table for the
|
||||
class.
|
||||
|
||||
WARNING;; `inplace_vptr_derived` must be used at each level of the hierarchy,
|
||||
and reflect the actual inheritance relationships. Otherwise, the vptrs will not
|
||||
be set correctly, and the wrong overriders may be called.
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
include::{example}/3/virtual_ptr_alt.cpp[tag=content]
|
||||
@@ -95,6 +99,5 @@ can be listed after the class being defined.
|
||||
The destructor of `inplace_vptr_derived` set the bases' vptrs back to the
|
||||
v-table for the bases, just like what C++ does for its native vptrs.
|
||||
|
||||
WARNING;; `inplace_vptr_derived` must be used at each level of the hierarchy,
|
||||
and reflect the actual inheritance relationships. Otherwise, the vptrs will not
|
||||
be set correctly, and the wrong overriders may be called.
|
||||
`inplace_vptr_base` and `inplace_vptr_derived` are aliased in `namespace
|
||||
boost::openmethod::aliases`.
|
||||
|
||||
Reference in New Issue
Block a user