mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 04:22:12 +00:00
improve documentation
This commit is contained in:
@@ -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 they’ve 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 couldn’t 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 don’t 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++ 2006–2020).
|
||||
|
||||
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.
|
||||
@@ -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]
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= xref:macros.adoc[Macro] BOOST_OPENMETHOD_DEFAULT_REGISTRY
|
||||
# BOOST_OPENMETHOD_DEFAULT_REGISTRY
|
||||
|
||||
Default value for Registry
|
||||
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++]
|
||||
----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:example: ../examples/rolex
|
||||
|
||||
## Friendship
|
||||
[#friendship]
|
||||
|
||||
Note;; This section uses overrider containers, described in the
|
||||
xref:headers.adoc[Headers] section.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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++]
|
||||
----
|
||||
|
||||
@@ -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++]
|
||||
----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user