diff --git a/doc/introduction.adoc b/doc/introduction.adoc deleted file mode 100644 index 10f9482..0000000 --- a/doc/introduction.adoc +++ /dev/null @@ -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. diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index bfbd5cc..6f79183 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -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] diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD.adoc index 2fac8a8..9952871 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD.adoc @@ -1,7 +1,7 @@ -= BOOST_OPENMETHOD +# BOOST_OPENMETHOD -### Synopsis +## Synopsis Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -9,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.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: diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_CLASSES.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_CLASSES.adoc index 26d3202..979a165 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_CLASSES.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_CLASSES.adoc @@ -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[]. @@ -11,7 +8,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -9,7 +8,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[` 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. diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc index 209b168..5febb82 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc @@ -1,4 +1,4 @@ -= xref:macros.adoc[Macro] BOOST_OPENMETHOD_DEFAULT_REGISTRY +# BOOST_OPENMETHOD_DEFAULT_REGISTRY Default value for Registry diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFINE_OVERRIDER.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFINE_OVERRIDER.adoc index 54daf06..e118ace 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFINE_OVERRIDER.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_DEFINE_OVERRIDER.adoc @@ -1,7 +1,7 @@ -## BOOST_OPENMETHOD_DEFINE_OVERRIDER +# BOOST_OPENMETHOD_DEFINE_OVERRIDER -### Synopsis +## Synopsis Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -9,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[` to enable runtime checks. -### Description +## Description See cpp:default_registry[] for details. diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_ID.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_ID.adoc index 6fe5d50..57eb26a 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_ID.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_ID.adoc @@ -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[]. @@ -11,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/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 diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDE.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDE.adoc index f29226e..eb64930 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDE.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDE.adoc @@ -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[]. @@ -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. diff --git a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDER.adoc b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDER.adoc index 9aa4e4b..fcedb6b 100644 --- a/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDER.adoc +++ b/doc/modules/ROOT/pages/BOOST_OPENMETHOD_OVERRIDER.adoc @@ -1,7 +1,7 @@ -## BOOST_OPENMETHOD_OVERRIDER +# BOOST_OPENMETHOD_OVERRIDER -### Synopsis +## Synopsis Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -9,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -10,7 +10,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -11,7 +9,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[]. @@ -9,7 +8,7 @@ Defined in link:{{BASE_URL}}/include/boost/openmethod/macros.hpp[ 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`, 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 diff --git a/doc/modules/ROOT/pages/core_api.adoc b/doc/modules/ROOT/pages/core_api.adoc index 4a433ea..391bd53 100644 --- a/doc/modules/ROOT/pages/core_api.adoc +++ b/doc/modules/ROOT/pages/core_api.adoc @@ -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++] ---- diff --git a/doc/modules/ROOT/pages/custom_rtti.adoc b/doc/modules/ROOT/pages/custom_rtti.adoc index 1d49118..8610312 100644 --- a/doc/modules/ROOT/pages/custom_rtti.adoc +++ b/doc/modules/ROOT/pages/custom_rtti.adoc @@ -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 diff --git a/doc/modules/ROOT/pages/error_handling.adoc b/doc/modules/ROOT/pages/error_handling.adoc index 2948b7f..5892d61 100644 --- a/doc/modules/ROOT/pages/error_handling.adoc +++ b/doc/modules/ROOT/pages/error_handling.adoc @@ -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 diff --git a/doc/modules/ROOT/pages/friendship.adoc b/doc/modules/ROOT/pages/friends.adoc similarity index 99% rename from doc/modules/ROOT/pages/friendship.adoc rename to doc/modules/ROOT/pages/friends.adoc index 7019b5d..9e94a47 100644 --- a/doc/modules/ROOT/pages/friendship.adoc +++ b/doc/modules/ROOT/pages/friends.adoc @@ -1,6 +1,6 @@ :example: ../examples/rolex -## Friendship +[#friendship] Note;; This section uses overrider containers, described in the xref:headers.adoc[Headers] section. diff --git a/doc/modules/ROOT/pages/headers.adoc b/doc/modules/ROOT/pages/headers.adoc index b1bbb6d..efd43ef 100644 --- a/doc/modules/ROOT/pages/headers.adoc +++ b/doc/modules/ROOT/pages/headers.adoc @@ -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 diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 799e097..91be5fe 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -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. diff --git a/doc/modules/ROOT/pages/introduction.adoc b/doc/modules/ROOT/pages/motivation.adoc similarity index 88% rename from doc/modules/ROOT/pages/introduction.adoc rename to doc/modules/ROOT/pages/motivation.adoc index 0a44588..895f7dd 100644 --- a/doc/modules/ROOT/pages/introduction.adoc +++ b/doc/modules/ROOT/pages/motivation.adoc @@ -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 diff --git a/doc/modules/ROOT/pages/multiple_dispatch.adoc b/doc/modules/ROOT/pages/multiple_dispatch.adoc index 5adb652..6ea0134 100644 --- a/doc/modules/ROOT/pages/multiple_dispatch.adoc +++ b/doc/modules/ROOT/pages/multiple_dispatch.adoc @@ -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 diff --git a/doc/modules/ROOT/pages/namespaces.adoc b/doc/modules/ROOT/pages/namespaces.adoc index 8ea250f..77b50d6 100644 --- a/doc/modules/ROOT/pages/namespaces.adoc +++ b/doc/modules/ROOT/pages/namespaces.adoc @@ -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++] ---- diff --git a/doc/modules/ROOT/pages/performance.adoc b/doc/modules/ROOT/pages/performance.adoc index 68b0f7d..f7c450e 100644 --- a/doc/modules/ROOT/pages/performance.adoc +++ b/doc/modules/ROOT/pages/performance.adoc @@ -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++] ---- diff --git a/doc/modules/ROOT/pages/ref_headers.adoc b/doc/modules/ROOT/pages/ref_headers.adoc index 82897ca..2064530 100644 --- a/doc/modules/ROOT/pages/ref_headers.adoc +++ b/doc/modules/ROOT/pages/ref_headers.adoc @@ -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[] +### link:{{BASE_URL}}/include/boost/openmethod/initialize.hpp[] Provides the cpp:initialize[] and cpp:finalize[] functions. This header is typically included in the translation unit containing `main`. Translation units diff --git a/doc/modules/ROOT/pages/ref_macros.adoc b/doc/modules/ROOT/pages/ref_macros.adoc index b4a0a48..c0c75ac 100644 --- a/doc/modules/ROOT/pages/ref_macros.adoc +++ b/doc/modules/ROOT/pages/ref_macros.adoc @@ -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. diff --git a/doc/modules/ROOT/pages/registries_and_policies.adoc b/doc/modules/ROOT/pages/registries_and_policies.adoc index 82150d2..bf611d2 100644 --- a/doc/modules/ROOT/pages/registries_and_policies.adoc +++ b/doc/modules/ROOT/pages/registries_and_policies.adoc @@ -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 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. diff --git a/doc/modules/ROOT/pages/shared_libraries.adoc b/doc/modules/ROOT/pages/shared_libraries.adoc index 754f7bb..92c99d0 100644 --- a/doc/modules/ROOT/pages/shared_libraries.adoc +++ b/doc/modules/ROOT/pages/shared_libraries.adoc @@ -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. diff --git a/doc/modules/ROOT/pages/smart_pointers.adoc b/doc/modules/ROOT/pages/smart_pointers.adoc index 074ffec..c1f9a29 100644 --- a/doc/modules/ROOT/pages/smart_pointers.adoc +++ b/doc/modules/ROOT/pages/smart_pointers.adoc @@ -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>` to `virtual_ptr`. 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>` to the method. +`virtual_ptr>` 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 diff --git a/doc/modules/ROOT/pages/virtual_ptr_alt.adoc b/doc/modules/ROOT/pages/virtual_ptr_alt.adoc index 49d2ed2..44ce930 100644 --- a/doc/modules/ROOT/pages/virtual_ptr_alt.adoc +++ b/doc/modules/ROOT/pages/virtual_ptr_alt.adoc @@ -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 diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index fd099ab..26c8e1c 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -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 diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index 9f794b1..5f9ce8c 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -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_`. //! //! @tparam T A type. template @@ -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 struct StripVirtualDecorator> { //! 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 -//! `` 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 +//! ``. 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 struct VirtualTraits { //! Class to use for dispatch. @@ -2817,6 +2826,11 @@ struct VirtualTraits { //! `virtual_ptr`, `std::shared_ptr`, //! `std::shared_ptr`, `virtual_ptr>`, //! 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. diff --git a/include/boost/openmethod/initialize.hpp b/include/boost/openmethod/initialize.hpp index 17098d1..b9d5672 100644 --- a/include/boost/openmethod/initialize.hpp +++ b/include/boost/openmethod/initialize.hpp @@ -1544,10 +1544,9 @@ void registry::compiler::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. diff --git a/include/boost/openmethod/preamble.hpp b/include/boost/openmethod/preamble.hpp index 0b2815a..656f0df 100644 --- a/include/boost/openmethod/preamble.hpp +++ b/include/boost/openmethod/preamble.hpp @@ -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 - static auto finalize(std::tuple opts) -> void; + static auto finalize(const std::tuple& options) -> void; }; #endif @@ -704,17 +711,12 @@ template 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 - static auto initialize(ForwardIterator first, ForwardIterator last) - -> std::pair; + template + static auto + initialize(const Context& ctx) -> std::pair; //! 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 + static auto finalize(const std::tuple& options) -> void; }; #endif @@ -934,9 +937,6 @@ class registry : detail::registry_base { template struct compiler; - // template - // friend compiler initialize(Options...); - //! Check that the registry is initialized. //! //! Check if `initialize` has been called for this registry, and report an