improve documentation

This commit is contained in:
Jean-Louis Leroy
2025-10-30 11:13:03 -04:00
parent 8933eb3b4f
commit 48e85546ce
36 changed files with 189 additions and 250 deletions

View File

@@ -1,67 +0,0 @@
# Introduction
Open-methods are similar to virtual functions, but they are not required to be
members of a class. By being both free and virtual, they provide a solution to
the Expression Problem:
> Given a set of types, and a set of operations on these types, is it possible
to add new operations on the existing types, and new types to the existing
operations, without modifying existing code?
Open-methods also address the banana-gorilla-shared problem:
> The problem with object-oriented languages is theyve got all this implicit
environment that they carry around with them. You wanted a banana but what you
got was a gorilla holding the banana and the entire shared. — Joe Armstrong,
creator of Erlang progamming language
As a bonus, open-methods can take more than one argument into account when
selecting the appropriate function to call - aka multiple dispatch. For that
reason, open-methods are often called multi-methods, but that term is
misleading, as it suggests that the feature is useful only when multiple
dispatch is needed. In reality,
https://openaccess.wgtn.ac.nz/articles/thesis/Multiple_Dispatch_in_Practice/16959112/1[it
has been observed] that, in large systems written in languages that support
multi-methods, most methods use single-dispatch. The real benefit is in the
solution to the Expression Problem.
Open-methods were introduced by the Common Lisp Object System, and they are
native to many languages: Clojure, Julia, Dylan, TADS, Cecil, Diesel, Nice, etc.
Bjarne Stroustrup wanted open-methods in C++ almost from the beginning. In D&E
he writes:
> I repeatedly considered a mechanism for a virtual function call based on more
than one object, often called multi-methods. I rejected multi-methods with
regret because I liked the idea, but couldnt find an acceptable form under
which to accept it. [...] Multi-methods is one of the interesting what-ifs of
C++. Could I have designed and implemented them well enough at the time? Would
their applications have been important enough to warrant the effort? What other
work might have been left undone to provide the time to design and implement
multi-methods? Since about 1985, I have always felt some twinge of regret for
not providing multi-methods (Stroustrup, 1994, The Design and Evolution of
C{plus}{plus}, 13.8).
Circa 2007, he and his PhD students Peter Pirkelbauer and Yuriy Solodkyy wrote a
series of papers and a prototype implementation based on the EDG compiler.
Unfortunately, open-methods never made it into the standard. Stroustrup bemoans,
in a more recent paper:
> In retrospect, I dont think that the object-oriented notation (e.g., x.f(y))
should ever have been introduced. The traditional mathematical notation f(x,y)
is sufficient. As a side benefit, the mathematical notation would naturally have
given us multi-methods, thereby saving us from the visitor pattern workaround
(Stroustrup, 2020, Thriving in a Crowded and ChangingWorld: C++ 20062020).
This library implements the features described in the
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2216.pdf[N2216 paper],
with some extensions:
* a mechanism for calling the next most specialized overrider
* support for smart pointers
* customization points for RTTI, error handling, tracing, smart pointers...
Multiple and virtual inheritance are supported, with the exception of repeated
inheritance.

View File

@@ -1,11 +1,12 @@
* xref:introduction.adoc[Introduction]
* 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]
* xref:motivation.adoc[Motivation]
* Basic Features
** xref:basics.adoc[Methods and Overriders]
** xref:performance.adoc[Performance]
** xref:smart_pointers.adoc[Smart Pointers]
** xref:headers.adoc[Header and Implementation Files]
** xref:namespaces.adoc[Namespaces]
** xref:friends.adoc[Friends]
** xref:multiple_dispatch.adoc[Multiple Dispatch]
* Advanced Features
** xref:core_api.adoc[Core API]
** xref:registries_and_policies.adoc[Registries and Policies]

View File

@@ -1,7 +1,7 @@
= BOOST_OPENMETHOD
# BOOST_OPENMETHOD
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -9,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
BOOST_OPENMETHOD(ID, (PARAMETERS...), RETURN_TYPE [, REGISTRY]);
```
### Description
## Description
Declares a method, called `ID`, with the given `PARAMETERS` and `RETURN_TYPE`,
and adds it to `REGISTRY`.
@@ -58,7 +58,7 @@ NOTE: The default value for `REGISTRY` is the value of
`BOOST_OPENMETHOD_DEFAULT_REGISTRY` at the point `<boost/openmethod/core.hpp>` is
included. Changing the value of this symbol has no effect after that point.
### Implementation Notes
## Implementation Notes
The macro creates several additional constructs:

View File

@@ -1,9 +1,6 @@
# BOOST_OPENMETHOD_CLASSES
[#BOOST_OPENMETHOD_CLASSES]
## BOOST_OPENMETHOD_CLASSES
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -11,7 +8,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
BOOST_OPENMETHOD_CLASSES(CLASSES...[, REGISTRY]);
```
### Description
## Description
Registers `CLASSES` in REGISTRY.

View File

@@ -1,7 +1,6 @@
# BOOST_OPENMETHOD_DECLARE_OVERRIDER
## BOOST_OPENMETHOD_DECLARE_OVERRIDER
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -9,7 +8,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
#define BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, (PARAMETERS...), RETURN_TYPE)
```
### Description
## Description
Declares an overrider for a method, but does not start its definition. This
macro can be used in header files.
@@ -33,7 +32,7 @@ Each `virtual_<T>` in the method's parameter list must have a corresponding `U`
parameter in the same position in the overrider's parameter list, such that `U`
is the same as `T`, or has `T` as an accessible unambiguous base.
### Implementation Notes
## Implementation Notes
The macro creates additional entities in the current scope.

View File

@@ -1,4 +1,4 @@
= xref:macros.adoc[Macro]&nbsp;BOOST_OPENMETHOD_DEFAULT_REGISTRY
# BOOST_OPENMETHOD_DEFAULT_REGISTRY
Default value for Registry

View File

@@ -1,7 +1,7 @@
## BOOST_OPENMETHOD_DEFINE_OVERRIDER
# BOOST_OPENMETHOD_DEFINE_OVERRIDER
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -9,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(ID, (PARAMETERS...), RETURN_TYPE)
```
### Description
## Description
Defines the body of an overrider declared with
xref:BOOST_OPENMETHOD_DECLARE_OVERRIDER.adoc[BOOST_OPENMETHOD_DECLARE_OVERRIDER].

View File

@@ -1,13 +1,13 @@
## BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
# BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
Enables runtime checks in cpp:default_registry[].
### Synopsis
## Synopsis
May be defined by a program before including
`<boost/openmethod/default_registry.hpp>` to enable runtime checks.
### Description
## Description
See cpp:default_registry[] for details.

View File

@@ -1,9 +1,7 @@
[#BOOST_OPENMETHOD_ID]
# BOOST_OPENMETHOD_ID
## BOOST_OPENMETHOD_ID
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -11,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
#define BOOST_OPENMETHOD_ID(ID) /* unspecified */
```
### Description
## Description
Generates a long, obfuscated name from a short name. All the other names
generated by macros are based on this name.

View File

@@ -1,9 +1,6 @@
# BOOST_OPENMETHOD_INLINE_OVERRIDE
[#BOOST_OPENMETHOD_INLINE_OVERRIDE]
## BOOST_OPENMETHOD_INLINE_OVERRIDE
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -13,7 +10,7 @@ BOOST_OPENMETHOD_INLINE_OVERRIDE(ID, (PARAMETERS...), RETURN_TYPE) {
}
```
### Description
## Description
`BOOST_OPENMETHOD_INLINE_OVERRIDE` performs the same function as
xref:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE], except that the

View File

@@ -1,9 +1,7 @@
[#BOOST_OPENMETHOD_OVERRIDE]
# BOOST_OPENMETHOD_OVERRIDE
## BOOST_OPENMETHOD_OVERRIDE
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -13,7 +11,7 @@ BOOST_OPENMETHOD_OVERRIDE(ID, (PARAMETERS...), RETURN_TYPE) {
}
```
### Description
## Description
`BOOST_OPENMETHOD_OVERRIDE` adds an overrider to a method.
@@ -48,7 +46,7 @@ and terminates the program.
* `has_next()`: returns `true` if the next most specialized overrider exists.
### Implementation Notes
## Implementation Notes
The macro creates additional entities in the current scope.

View File

@@ -1,7 +1,7 @@
## BOOST_OPENMETHOD_OVERRIDER
# BOOST_OPENMETHOD_OVERRIDER
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -9,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
#define BOOST_OPENMETHOD_OVERRIDER(ID, (PARAMETERS...), RETURN_TYPE)
```
### Description
## Description
Expands to the specialization of the class template that contains the overrider
for with the given name, parameter list and return type.

View File

@@ -1,7 +1,7 @@
## BOOST_OPENMETHOD_OVERRIDERS
# BOOST_OPENMETHOD_OVERRIDERS
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -10,7 +10,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
BOOST_PP_CAT(BOOST_OPENMETHOD_ID(ID), _overriders)
```
### Description
## Description
`BOOST_OPENMETHOD_OVERRIDERS` expands to the name of the class template that
contains the overriders for all the methods with a given name.

View File

@@ -1,9 +1,7 @@
[#BOOST_OPENMETHOD_REGISTER]
# BOOST_OPENMETHOD_REGISTER
## BOOST_OPENMETHOD_REGISTER
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -11,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
BOOST_OPENMETHOD_REGISTER(TYPE);
```
### Description
## Description
Creates a registrar for `TYPE`, i.e. a static `TYPE` object with a unique
generated name. At static initialization time, the object adds itself to a list:

View File

@@ -1,7 +1,6 @@
# BOOST_OPENMETHOD_TYPE
= BOOST_OPENMETHOD_TYPE
### Synopsis
## Synopsis
Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmethod/macros.hpp>].
@@ -9,7 +8,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[<boost/openmeth
BOOST_OPENMETHOD_TYPE(ID, (PARAMETERS...), RETURN_TYPE [, REGISTRY]);
```
### Description
## Description
Expands to the core cpp:method[`method`] specialization created by
xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] called with the same arguments.

View File

@@ -1,6 +1,6 @@
:exampledir: ../example
## Open-Methods 101
[#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 from a
@@ -22,7 +22,7 @@ BOOST_OPENMETHOD(
postfix, (virtual_ptr<const Node> node, std::ostream& os), void);
```
This method is called `postfix`. It takes one virtual parameter, `node`, and one
`postfix` is the method's name. It takes one virtual parameter, `node`, and one
non-virtual parameter, `os`. It returns `void`. The macro generates the
following function:
@@ -61,15 +61,15 @@ This one calls `postfix` recursively to print the left and right
sub-expressions. Note that we call the method just like an ordinary function.
`postfix` expects a `virtual_ptr<const Node>`, and we are passing it a _plain_
_reference_ to a `Plus` object. This works because `virtual_ptr` has conversion
constructors from plain references or pointers to an object, or from other
`virtual_ptr`{empty}s to compatible classes.
_reference_ to a `Node` object. This works because `virtual_ptr` has conversion
constructors from plain references or pointers to objects, or from other
`virtual_ptr`{empty}s.
There are two more things we need to do.
OpenMethod is a library, not a compiler. It needs to be made aware of all the
OpenMethod is a library, not a compiler. It needs to be informed of all the
classes that may be used as virtual parameters, and in method calls, and their
inheritance relationships. We do this with the
inheritance relationships. We provide that information with the
xref:BOOST_OPENMETHOD_CLASSES.adoc[BOOST_OPENMETHOD_CLASSES] macro:
@@ -84,8 +84,8 @@ direct base of a class must appear together with it in at least one call to
inheritance lattice.
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.
the standard C++ sense, i.e. they must 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,6 +1,6 @@
:example: ../examples/core_api/1
## Core API
[#core_api]
OpenMethod provides a macro-free interface: the core API. This is useful in
certain situations, for example when combining open-methods and templates.
@@ -25,8 +25,8 @@ method, `prefix`, which writes expressions in prefix notation. It would have the
same signature. Without the identifier argument, `prefix` and `postfix` would be the
same method.
The exact name of the identifier class does not matter, as long as it is unique enough.
The class needs not be defined, only declared.
The exact name of the identifier class does not matter. The class needs not be
defined, only declared.
Inventing identifier class names can get tedious, so OpenMethod provides a macro
for that: xref:BOOST_OPENMETHOD_ID.adoc[BOOST_OPENMETHOD_ID]. Let's use it:
@@ -58,9 +58,12 @@ functions passed as template arguments to the method.
include::{example}/core_api.cpp[tag=variable_overrider]
----
Once again we find ourselves inventing a name that will not be used again. In
C++26, we will probably have the Python-like `_` for this. In the meantime, we
can use a small macro:
Once again we find ourselves inventing a name for a single use. In C++26, we
will probably have the Python-like `_` for this.
We can also use a small macro:
xref:BOOST_OPENMETHOD_REGISTER.adoc[BOOST_OPENMETHOD_REGISTER]. It takes a
class, and instantiates a static object with an obfuscated name:
[source,c++]
----
@@ -91,7 +94,7 @@ The primary purpose of the core API is to make open-methods inter-operate with
templates.
`Plus` and `Times` are obvious candidates for templatization. They only differ
by the operation they perform, and we already have templates fore that in the
by the operation they perform, and we already have templates for that in the
standard library:
[source,c++]
@@ -118,8 +121,8 @@ include::{example}/core_api.cpp[tag=postfix_binary]
----
Macro xref:BOOST_OPENMETHOD_TYPE.adoc[BOOST_OPENMETHOD_TYPE] takes the same
parameters as `BOOST_OPENMETHOD`, and expands to the core method type. That is
how we access its nested `overrider` class template:
parameters as `BOOST_OPENMETHOD`, and expands to the core cpp:method[method]
instance. That is how we access its nested `overrider` class template:
[source,c++]
----

View File

@@ -1,7 +1,7 @@
:example: ../examples/custom_rtti
## Custom RTTI
[#custom_rtti]
The original motivation for the policy mechanism is to make it possible to
interface OpenMethod with custom RTTI systems.
@@ -106,25 +106,26 @@ still work.
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 `rtti::default`. 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)".
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)".
`rtti::default` also provides a default implementation for `type_index`, which
simply returns its argument.
cpp:rtti::defaults[] also provides a default implementation for `type_index`,
which simply returns its argument.
Now we need a policy to get a v-table pointer from an object. Our RTTI system
has an interesting property: its type ids are monotonically allocated in a
small, dense range. It means that we can use them as straight indexes in a
vector. `vptr_vector` is perfect for that. So here is our registry:
small, dense range. It means that we can use them as indexes in a vector.
`vptr_vector` is perfect for that. So here is our registry:
[source,c++]
----
include::{example}/1/custom_rtti.cpp[tag=registry]
----
Defining macro `BOOST_OPENMETHOD_DEFAULT_REGISTRY` sets the default registry
used by all library components that need one.
Defining macro
xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[BOOST_OPENMETHOD_DEFAULT_REGISTRY]
sets the default registry used by all library components that need one.
Next, we include the main header.
Because `BOOST_OPENMETHOD_DEFAULT_REGISTRY` is defined, its value is used

View File

@@ -1,5 +1,5 @@
## Error Handling
[#error_handling]
Errors can occur during `initialize`, or during method dispatch, if the
method's registry contains the `runtime_checks` policy. If the registry

View File

@@ -1,6 +1,6 @@
:example: ../examples/rolex
## Friendship
[#friendship]
Note;; This section uses overrider containers, described in the
xref:headers.adoc[Headers] section.

View File

@@ -1,6 +1,6 @@
:example: ../examples/rolex
## Headers
[#Headers]
Typically, `BOOST_OPENMETHOD` is used in headers, while
`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` are used in

View File

@@ -1,5 +1,6 @@
:example: ../examples
[#boost-openmethod]
= Boost.OpenMethod
Boost.OpenMethod implements open-(multi-)methods in C++17 and above.
@@ -72,18 +73,18 @@ manager.
Thanks to the members of the Boost community who posted a formal review:
* Joaquin M López Muñoz
* Andrzej Krzemienski
* Christian Mazakas
* Joaquin M López Muñoz
* Klemens Morgenstern
* Ruben Perez
* Yannick Le Goc
* Klemens Morgenstern
* Andrzej Krzemienski
Also thanks to Steven Watanabe for his cogent feedback and advice, and all
the people who posted remarks and suggestions.

View File

@@ -1,5 +1,5 @@
## Introduction
[#motivation]
Consider a class hierarchy that represents and evaluates arithmetic
expressions:
@@ -84,9 +84,12 @@ Julia, Cecil, TADS, and others, have multi-methods.
But wait! What do multi-methods have to do with our problem? There is no
multiple dispatch going on here! The thing is, "multi-methods" is not a very
good name. Even in languages that support them, multi-methods tend to use single
dispatch only. For that reason, we prefer the term "open-methods", to emphasize
the important feature: openness. Multiple dispatch is just a bonus.
good name. Even in languages that support them, many "multi-methods" have a
single virtual parameter - they are uni-methods
footnote:[https://figshare.com/articles/thesis/Multiple_Dispatch_in_Practice/16959112[Multiple
dispatch in practice], Radu Muschevici, 2008.]! For that reason, we prefer the
term "open-methods", to emphasize the important feature: openness. Multiple
dispatch is just a bonus.
An open-method is like a virtual function, but it exists outside of a class, as
a free-standing function. We can create all the open-methods we need, without

View File

@@ -1,12 +1,12 @@
:example: ../examples
## Multiple Dispatch
[#multiple_dispatch]
A method can have more than one virtual parameter. This is often called
"multi-methods" or "multiple dispatch". All the virtual parameters participate
"multi-method" 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.
into account the arguments dynamic types.
Multiple dispatch is occasionally useful. When it is needed, it can be difficult
to implement correctly and efficiently by hand. For example, given the following

View File

@@ -1,9 +1,9 @@
:example: ../examples/rolex
## Namespaces
[#namespaces]
Note;; This section uses overrider containers, described in the
xref:headers.adoc[Headers] section.
xref:headers.adoc[Headers and Implementation Files] section.
xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] defines a method in the current
namespace. xref:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE] works
@@ -13,9 +13,9 @@ 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
namespaces. This must be taken into account when calling an overrider
explicitly. Let's put Employee and Salesman in their own namespaces:
same method can have overriders in multiple containers, 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++]
----

View File

@@ -1,5 +1,5 @@
## Performance
[#performance]
Open-methods can be as fast as ordinary virtual member functions when
compiled with optimization.
@@ -96,9 +96,10 @@ 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
in place of plain pointers or references, as much as possible. Here is the Node
example, rewritten to use `virtual_ptr`{empty}s thoughout:
Code that incorporates open-methods in its design 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:
[source,c++]
----

View File

@@ -46,7 +46,7 @@ effect as including `boost/openmethod.hpp`, which is shorter.
Includes `core.hpp` and `macros.hpp`.
[#initialize]
### link:{{BASE_URL}}/include/boost/initialize.hpp[<boost/initialize.hpp>]
### link:{{BASE_URL}}/include/boost/openmethod/initialize.hpp[<boost/initialize.hpp>]
Provides the cpp:initialize[] and cpp:finalize[] functions. This header is
typically included in the translation unit containing `main`. Translation units

View File

@@ -1,6 +1,5 @@
[#macros]
= xref:macros.adoc[Macros]
:mrdocs:
[#ref_macros]
= xref:ref_macros.adoc[Macros]
The following macros - in particular the first three- are sufficient for most
uses of the library.

View File

@@ -1,5 +1,5 @@
## Registries and Policies
[#registries_and_policies]
Methods are scoped in a registry. A method can only reference classes in the
same registry. If a class is used as a virtual parameter in methods using
@@ -20,9 +20,9 @@ A registry has a collection of _policies_. Each policy belongs to a policy
category. A registry may contain at most one policy of each category. Policies
control how type information is obtained, how vptrs are acquired, how errors are
handled and reported, etc. While the behavior of cpp:initialize[] can be
customized via options, policies are primarily involved in in method dispatch.
customized via options, policies are primarily involved in method dispatch.
Policies are placed in the `boost::openmethod::policies` namespace.
Policies are placed in the cpp:boost::openmethod::policies[] namespace.
`default_registry` contains the following policies:
@@ -40,11 +40,11 @@ Policies are placed in the `boost::openmethod::policies` namespace.
| type_hash
| fast_perfect_hash
| hash type id to an index in a vector
| hashes type id to an index in a vector
| error_handler
| default_error_handler
| handles errors
| calls an overridable handler function
| output
| stderr_output
@@ -61,7 +61,7 @@ registrations that could not be caught by `initialize`.
The library provides another predefined registry: cpp:indirect_registry[]. It is
useful when shared libraries are dynamically loaded at runtime, and add methods
and overriders across program and shared library boundaries. See the section
about shared_libraries.adoc[shared libraries].
about xref:shared_libraries.adoc[shared libraries].
Registries can be created from scratch, using the `registry` template. Here is
the definition of `default_registry`, copied from
@@ -99,9 +99,9 @@ struct indirect_registry : default_registry::with<policies::indirect_vptr> {};
Policies are implemented as unary
https://www.boost.org/doc/libs/1_89_0/libs/mp11/doc/html/mp11.html[Boost.MP11
quoted metafunctions]. A policy is an ordinary class that contains a nested
class template, called `fn` which is instantiated by the registry, passing
itself as the template argument. The reason for this mechanism is to allow
policies that have static data members to give each registry its own copy.
class template `fn`, which is instantiated by the registry, passing itself as
the single template argument. The reason for this mechanism is to allow policies
to have static data members, which each registry must have its own set of.
`vptr_vector`, for example, stores v-table pointers in a `std::vector`. If it is
used in two different registries, it needs to use two different vectors, one for
each registry.

View File

@@ -1,6 +1,6 @@
:shared: ../examples/shared_libs
## Shared Libraries
[#shared_libraries]
This section discusses how OpenMethod interoperates with shared libraries on
Linux, other POSIX-like platforms, and Windows.

View File

@@ -1,5 +1,5 @@
## Smart Pointers
[#smart_pointers]
If we want maximum performance, we want to use `virtual_ptr`{empty}s in place of
ordinary pointers or references. However, we may also want to use smart pointers
@@ -20,10 +20,10 @@ v-table pointer.
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 footnote:[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.
`virtual_ptr<std::shared_ptr<const Matrix>>` to the method.].
The reverse conversion, from plain to smart, does not exist, because it would
have the potential to accidentally create smart pointers. Likewise, a smart

View File

@@ -1,5 +1,6 @@
:example: ../examples/virtual_ptr_alt
[#virtual_ptr_alt]
## Alternatives to virtual_ptr
Virtual arguments can also be passed as plain references, plain pointers, or

View File

@@ -17,16 +17,13 @@ implementation-defined:
- 'boost::openmethod::detail'
inaccessible-members: never
inaccessible-bases: never
# see-below:
# - 'boost::urls::format_arg'
# implementation-defined:
# - 'boost::openmethod::detail::**'
exclude-symbols:
- 'boost::openmethod::registry::compiler'
- 'boost::openmethod::registry::initialize'
- 'boost::openmethod::registry::finalize'
- 'boost::openmethod::boost_openmethod_bases'
- 'boost::openmethod::boost_openmethod_registry'
- 'boost::openmethod::registry::compiler'
sort-members: false
# sort-namespace-members-by: location

View File

@@ -192,7 +192,10 @@ using virtual_types = boost::mp11::mp_transform<
BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
//! Remove virtual_<> decorator from a type (exposition only).
//! Removes the virtual_<> decorator, if present (exposition only).
//!
//! Provides a nested `type` equal to `T`. The template is specialized for
//! `virtual_<T>`.
//!
//! @tparam T A type.
template<typename T>
@@ -201,7 +204,11 @@ struct StripVirtualDecorator {
using type = T;
};
//! Remove virtual_<> decorator from a type (exposition only).
//! Removes the virtual_<> decorator (exposition only).
//!
//! Provides a nested `type` equal to `T`.
//!
//! @tparam T A type.
template<typename T>
struct StripVirtualDecorator<virtual_<T>> {
//! Same as `T`.
@@ -213,20 +220,20 @@ BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
// =============================================================================
// virtual_traits
//! Traits for types that can be used as virtual arguments.
//! Traits for types used as virtual parameters.
//!
//! `virtual_traits` must be specialized for each type that can be used as a
//! virtual parameters. It enables methods to:
//! @li find the type of the object the argument refers to (e.g. `Cat` from
//! `Cat&`)
//! @li obtain a non-modifiable reference to that object (e.g. a `const Cat&` from
//! `Cat&`)
//! @li cast the argument to another type (e.g. cast a `Animal&` to a `Cat&`)
//!
//! @li find the type of the object the argument refers to (e.g. `Node` from
//! `Node&`)
//! @li obtain a non-modifiable reference to that object (e.g. a `const Node&` from
//! `Node&`)
//! @li cast the argument to another type (e.g. cast a `Node&` to a `Plus&`)
//!
//! @par Requirements
//! Specializations of `virtual_traits` must implement the members deswcribed to the @ref VirtualTraits
//! blueprint.
//!
//! Specializations of `virtual_traits` must provide the members described to
//! the @ref VirtualTraits blueprint.
//!
//! @tparam T A type referring (in the broad sense) to an instance of a class.
//! @tparam Registry A @ref registry.
@@ -2067,20 +2074,19 @@ struct validate_method_parameter<
//!
//! `Registry` is an instantiation of class template @ref registry. Methods may
//! use only classes that have been registered in the same registry as virtual
//! parameters and arguments. The registry also policies that influence several
//! aspects of the dispatch mechanism - for example, how to obtain a v-table
//! pointer for an object, how to report errors, whether to perform sanity
//! checks, etc.
//! parameters and arguments. The registry also contains a set of policies that
//! influence several aspects of the dispatch mechanism - for example, how to
//! acquire a v-table pointer for an object, how to report errors, whether to
//! perform sanity checks, etc.
//!
//! The default value of `Registry` is the preprocessor symbol
//! `BOOST_OPENMETHOD_DEFAULT_REGISTRY`. It can be defined before including the
//! `<boost/openmethod/core.hpp>` header to override the default registry.
//! Setting this symbol after including `core.hpp` has no effect.
//! The default value for `Registry` is @ref default_registry, but it can be
//! overridden by defining the preprocessor symbol
//! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}, *before* including
//! `<boost/openmethod/core.hpp>`. Setting the symbol afterwards has no effect.
//!
//! Specializations of `method` have a single instance: the static member `fn`,
//! a function object whose `operator()` is used to call the method and forward
//! to the appropriate overrider. It is selected in the same way as overloaded
//! function resolution:
//! which has an `operator()` that forwards to the appropriate overrider. It is
//! selected in the same way as overloaded function resolution:
//!
//! 1. Form the set of all applicable overriders. An overrider is applicable
//! if it can be called with the arguments passed to the method.
@@ -2088,8 +2094,8 @@ struct validate_method_parameter<
//! 2. If the set is empty, call the error handler (if present in the
//! registry), then terminate the program with `abort`.
//!
//! 3. Remove the overriders that are dominated by other overriders in the
//! set. Overrider A dominates overrider B if any of its virtual formal
//! 3. Remove the overriders that are dominated by other overriders in the set.
//! Overrider A dominates overrider B if at least one of its virtual formal
//! parameters is more specialized than B's, and if none of B's virtual
//! parameters is more specialized than A's.
//!
@@ -2806,6 +2812,9 @@ using boost::openmethod::virtual_ptr;
//!
//! Specializations of @ref virtual_traits must implement the members listed
//! here.
//!
//! @tparam T The type of a virtual parameter of a method.
//! @tparam Registry A @ref registry.
template<typename T, class Registry>
struct VirtualTraits {
//! Class to use for dispatch.
@@ -2817,6 +2826,11 @@ struct VirtualTraits {
//! `virtual_ptr<const Class>`, `std::shared_ptr<Class>`,
//! `std::shared_ptr<const Class>`, `virtual_ptr<std::shared_ptr<Class>>`,
//! etc.
//!
//! @par Requirements
//!
//! `virtual_type` must be an alias to an *unadorned* *class* type, *not*
//! cv-qualified.
using virtual_type = detail::unspecified;
//! Returns a reference to the object to use for dispatch.

View File

@@ -1544,10 +1544,9 @@ void registry<Policies...>::compiler<Options...>::print(
//! Initialize a registry.
//!
//! Initialize the @ref registry passed as an explicit function template
//! Initialize the @ref registry passed as an explicit function template
//! argument, or @ref default_registry if the registry is not specified. The
//! default can be changed by defining
//! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}.
//! default can be changed by defining {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}.
//! Option objects can be passed to change the behavior of the function.
//! Currently two options exist:
//! @li @ref trace Enable tracing of the initialization process.

View File

@@ -376,12 +376,19 @@ struct unspecified {};
//! Blueprint for a lightweight output stream (exposition only).
//!
//! Classes used as output streams in policies must provide the free functions
//! described on this page.
//! Classes used as output streams in policies must provide the operations
//! described on this page, either as members or as free functions.
struct LightweightOutputStream {
//! Writes a null-terminated string to the stream.
LightweightOutputStream& operator<<(const char* str);
//! Writes a string view to the stream.
LightweightOutputStream& operator<<(const std::string_view& view);
//! Writes a pointer value to the stream.
LightweightOutputStream& operator<<(const void* value);
//! Writes a size_t value to the stream.
LightweightOutputStream& operator<<(std::size_t value);
};
@@ -662,9 +669,9 @@ struct VptrFn {
//!
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param options Zero or more option objects.
//! @param options A tuple of option objects.
template<class... Options>
static auto finalize(std::tuple<Options...> opts) -> void;
static auto finalize(const std::tuple<Options...>& options) -> void;
};
#endif
@@ -704,17 +711,12 @@ template<class Registry>
struct TypeHashFn {
//! Initialize the hash table.
//!
//! @tparam ForwardIterator An iterator to a range of @ref
//! InitializeClass objects.
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param first An iterator to the beginning of the range.
//! @param last An iterator to the end of the range.
//! @param options Zero or more option objects.
//! @tparam Context A class that conforms to the @ref InitializeContext
//! blueprint.
//! @return A pair containing the minimum and maximum hash values.
template<typename ForwardIterator>
static auto initialize(ForwardIterator first, ForwardIterator last)
-> std::pair<std::size_t, std::size_t>;
template<class Context>
static auto
initialize(const Context& ctx) -> std::pair<std::size_t, std::size_t>;
//! Hash a `type_id`.
//!
@@ -726,10 +728,11 @@ struct TypeHashFn {
//!
//! This function is optional.
//!
//! @tparam Options... Zero or more option types, deduced from the function
//! arguments.
//! @param options Zero or more option objects.
static auto finalize() -> void;
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param options A tuple of option objects.
template<class... Options>
static auto finalize(const std::tuple<Options...>& options) -> void;
};
#endif
@@ -934,9 +937,6 @@ class registry : detail::registry_base {
template<class... Options>
struct compiler;
// template<class Registry, typename... Options>
// friend compiler<Options...> initialize(Options...);
//! Check that the registry is initialized.
//!
//! Check if `initialize` has been called for this registry, and report an