From 9ae95df2affc7432b4cc56dd4e04039838043c32 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Sun, 19 Oct 2025 16:29:17 -0400 Subject: [PATCH] small doc improvements --- doc/inplace_vptr.adoc | 77 ------------------- .../ROOT/examples/rolex/1/employee.cpp | 2 + .../ROOT/examples/rolex/1/salesman.cpp | 2 + doc/modules/ROOT/examples/rolex/2/roles.hpp | 1 - doc/modules/ROOT/nav.adoc | 4 +- doc/modules/ROOT/pages/basics.adoc | 5 +- doc/modules/ROOT/pages/custom_rtti.adoc | 2 +- doc/modules/ROOT/pages/headers.adoc | 25 +++--- doc/modules/ROOT/pages/namespaces.adoc | 7 +- doc/modules/ROOT/pages/performance.adoc | 11 ++- doc/modules/ROOT/pages/ref_headers.adoc | 2 +- doc/modules/ROOT/pages/shared_libraries.adoc | 4 +- doc/modules/ROOT/pages/smart_pointers.adoc | 36 +++++---- doc/modules/ROOT/pages/virtual_ptr_alt.adoc | 13 ++-- 14 files changed, 63 insertions(+), 128 deletions(-) delete mode 100644 doc/inplace_vptr.adoc diff --git a/doc/inplace_vptr.adoc b/doc/inplace_vptr.adoc deleted file mode 100644 index 1c40383..0000000 --- a/doc/inplace_vptr.adoc +++ /dev/null @@ -1,77 +0,0 @@ - -## inplace_vptr - -### Synopsis - -Defined in . - -```c++ -namespace boost::openmethod { - -template -class inplace_vptr { - protected: - inplace_vptr(); - ~inplace_vptr(); - friend auto boost_openmethod_vptr(const Class& obj) -> vptr_type; -}; - -template -class inplace_vptr { - protected: - inplace_vptr(); - ~inplace_vptr(); - friend auto boost_openmethod_vptr(const Class& obj) -> vptr_type; - // if sizeof(MoreBases...) > 0 -}; - -} // namespace boost::openmethod -``` - -### Description - -`inplace_vptr` is a CRTP class template that embeds and manages a vptr across a -class hierarchy. - -If `Class` has no `Bases`, `inplace_vptr` adds a `boost_openmethod_vptr` private -member to `Class`. In either case, it sets the vptr to the v-table of `Class` -from `Policy`. It also creates a `boost_openmethod_vptr` friend function that -takes a a `const Class&` and returns the embedded vptr. - -If `Class` has more than one base, the `boost_openmethod_vptr` friend -function is also created. It returns one of the embedded vptrs (it doesn't -matter which one, as they all have the same value). This is to resolve -ambiguities - -As part of its implementation, `inplace_vptr` may also declare one or two free -functions (`boost_openmethod_policy` and `boost_openmethod_bases`) at certain -levels of the hierarchy. - -### Members - -#### constructor - -```c++ -inplace_vptr(); -``` - -Sets the vptr to the v-table for Class, obtained from `Policy`. If `Policy` -contains `indirect_vptr`, an additional level of indirection is added, thus -preserving the validity of the pointer across calls to `initialize`. - - -#### destructor - -```c++ -~inplace_vptr(); -``` - -For each `Base`, sets the vptr to the v-table for that base. - -#### Free Functions - -```c++ -auto boost_openmethod_vptr(const Class& obj) -> vptr_type; -``` - -Returns the vptr embedded in `obj`. diff --git a/doc/modules/ROOT/examples/rolex/1/employee.cpp b/doc/modules/ROOT/examples/rolex/1/employee.cpp index a2dec09..30e07ed 100644 --- a/doc/modules/ROOT/examples/rolex/1/employee.cpp +++ b/doc/modules/ROOT/examples/rolex/1/employee.cpp @@ -4,6 +4,8 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) // tag::content[] +// employee.cpp + #include "roles.hpp" #include diff --git a/doc/modules/ROOT/examples/rolex/1/salesman.cpp b/doc/modules/ROOT/examples/rolex/1/salesman.cpp index 8eef162..1476356 100644 --- a/doc/modules/ROOT/examples/rolex/1/salesman.cpp +++ b/doc/modules/ROOT/examples/rolex/1/salesman.cpp @@ -4,6 +4,8 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) // tag::content[] +// salesman.cpp + #include "roles.hpp" #include diff --git a/doc/modules/ROOT/examples/rolex/2/roles.hpp b/doc/modules/ROOT/examples/rolex/2/roles.hpp index 5865163..31d9359 100644 --- a/doc/modules/ROOT/examples/rolex/2/roles.hpp +++ b/doc/modules/ROOT/examples/rolex/2/roles.hpp @@ -22,6 +22,5 @@ BOOST_OPENMETHOD(pay, (boost::openmethod::virtual_ptr), double); BOOST_OPENMETHOD_DECLARE_OVERRIDER( pay, (boost::openmethod::virtual_ptr), double); - #endif // ROLES_HPP // end::content[] diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 6bb2a39..a4aff8f 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -1,12 +1,12 @@ * xref:introduction.adoc[Introduction] -* xref:basics.adoc[Basics] +* xref:basics.adoc[Open-Methods 101] * xref:performance.adoc[Performance] * xref:smart_pointers.adoc[Smart Pointers] * xref:headers.adoc[Headers] * xref:namespaces.adoc[Namespaces] * xref:friendship.adoc[Friendship] * xref:multiple_dispatch.adoc[Multiple Dispatch] -* Advanced Features +* xref:advanced_features.adoc[Advanced Features] ** xref:core_api.adoc[Core API] ** xref:registries_and_policies.adoc[Registries and Policies] ** xref:custom_rtti.adoc[Custom RTTI] diff --git a/doc/modules/ROOT/pages/basics.adoc b/doc/modules/ROOT/pages/basics.adoc index ad7fd27..59590fe 100644 --- a/doc/modules/ROOT/pages/basics.adoc +++ b/doc/modules/ROOT/pages/basics.adoc @@ -1,6 +1,6 @@ :exampledir: ../example -## Basics +## Open-Methods 101 An _open-method_ is a free-standing function that takes one or more _virtual_ _parameters_. When it is called, it forwards to an _overrider_ selected from a @@ -11,7 +11,8 @@ with one virtual parameter is equivalent to a virtual function - with one big difference: it exists outside of classes. A virtual parameter is in the form `virtual_ptr`. It is a pointer-like -class that points to an instance of `Class`. +class that points to an instance of `Class`. `virtual_ptr` is defined in the library's main namespace, +`boost::openmethod`. To create an open-method that implements the `postfix` operation, we use the xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] macro: diff --git a/doc/modules/ROOT/pages/custom_rtti.adoc b/doc/modules/ROOT/pages/custom_rtti.adoc index 3276ca9..1d49118 100644 --- a/doc/modules/ROOT/pages/custom_rtti.adoc +++ b/doc/modules/ROOT/pages/custom_rtti.adoc @@ -50,7 +50,7 @@ struct std_rtti : rtti { This template is required. * `static_type` is called during class registration, by - `virtual_ptr`{empty}'s "final" constructs. + `virtual_ptr`{empty}s "final" constructs. It is also called to set `bad_call::method`. This function is required. diff --git a/doc/modules/ROOT/pages/headers.adoc b/doc/modules/ROOT/pages/headers.adoc index 2e1f90f..b1bbb6d 100644 --- a/doc/modules/ROOT/pages/headers.adoc +++ b/doc/modules/ROOT/pages/headers.adoc @@ -2,30 +2,27 @@ ## Headers -Typically, `BOOST_OPENMETHOD` go in headers, while -`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` go in +Typically, `BOOST_OPENMETHOD` is used in headers, while +`BOOST_OPENMETHOD_CLASSES` and `BOOST_OPENMETHOD_OVERRIDE` are used in implementation files. -Let's use a payroll application as an example. -We have two roles: `employee` and `salesman`, and a `pay` method that -computes the monthly pay of an employee. -We want to override and call `pay` from from multiple translation units, so -we put it in a header: +Let's use a payroll application as an example. We have two roles: `Employee` and +`Salesman`, and a `pay` method that computes the monthly pay of an employee. We +want to override and call `pay` from from multiple translation units, so we put +it in a header: [source,c++] ---- include::{example}/1/roles.hpp[tag=content] ---- -`BOOST_OPENMETHOD` _defines_ an inline function, so it can be called only -once in a translation unit. -The include guards see to this. +`BOOST_OPENMETHOD` _defines_ an inline function, so it can be called only once +in a translation unit. The include guards see to this. Let's write the override for "just" employees - they get a fixed salary. -`BOOST_OPENMETHOD_OVERRIDE` _adds_ an overrider to the method. -We don't want to add the same multiple times; that would create an -ambiguity. -Thus it should go in an implementation file: +`BOOST_OPENMETHOD_OVERRIDE` _adds_ an overrider to the method. We don't want to +add the same multiple times; that would create an ambiguity. Thus it should go +in an implementation file: [source,c++] ---- diff --git a/doc/modules/ROOT/pages/namespaces.adoc b/doc/modules/ROOT/pages/namespaces.adoc index c6efbfe..8ea250f 100644 --- a/doc/modules/ROOT/pages/namespaces.adoc +++ b/doc/modules/ROOT/pages/namespaces.adoc @@ -8,8 +8,9 @@ xref:headers.adoc[Headers] section. xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD] defines a method in the current namespace. xref:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE] works _across_ namespaces. Overriders are not required to be in the same namespace as -the method they override. The macro adds the overrider a method that can be -called with the same arguments, possibly located via argument dependant lookup. +the method they override. The macro adds the overrider to a method that can be +called with the same arguments as the overrider, possibly located via argument +dependant lookup. Overrider containers are added to the current namespace. It follows that the same method can have overriders in several different container, in different @@ -45,5 +46,5 @@ namespace: [source,c++] ---- -include::{example}/3/salesman.cpp[tag=content] +include::{example}/4/salesman.cpp[tag=content] ---- diff --git a/doc/modules/ROOT/pages/performance.adoc b/doc/modules/ROOT/pages/performance.adoc index 86fb9cb..68b0f7d 100644 --- a/doc/modules/ROOT/pages/performance.adoc +++ b/doc/modules/ROOT/pages/performance.adoc @@ -47,16 +47,15 @@ This compiles to (variable names are shortened for readability): mov rdx, rsi mov rcx, qword ptr [rip + vptr_vector_vptrs] mov rdi, qword ptr [rcx + 8*rdi] - mov rcx, qword ptr [rip + fn+88] + mov rcx, qword ptr [rip + postfix::fn+88] mov rcx, qword ptr [rdi + 8*rcx] mov rsi, rax jmp rcx # TAILCALL ---- This is quite a few instructions more. Upon closer examination, we see that many -are memory reads, independent of one another; they can be thus executed in -parallel. For example, the first three instructions would execute simultaneously -on a modern CPU. +are memory reads, independent of one another; they can thus be executed in +parallel. For example, the first three instructions can execute simultaneously. llvm-mca estimates a throughput of 4 cycles per dispatch. However, the difference is amortized by the time spent passing the arguments and returning @@ -97,9 +96,9 @@ jmp rax # TAILCALL `virtual_ptr` arguments are passed through the method call, to the overrider, which can use them to make further method calls. -A program designed with open-methods in mind should use `virtual_ptr`{empty}'s +A program designed with open-methods in mind should use `virtual_ptr`{empty}s in place of plain pointers or references, as much as possible. Here is the Node -example, rewritten to use `virtual_ptr`{empty}'s thoughout: +example, rewritten to use `virtual_ptr`{empty}s thoughout: [source,c++] ---- diff --git a/doc/modules/ROOT/pages/ref_headers.adoc b/doc/modules/ROOT/pages/ref_headers.adoc index 2dd0932..680a4aa 100644 --- a/doc/modules/ROOT/pages/ref_headers.adoc +++ b/doc/modules/ROOT/pages/ref_headers.adoc @@ -22,7 +22,7 @@ parameters: * xref:#std_unique_ptr[`boost/openmethod/interop/std_unique_ptr.hpp`] to use `std::unique_ptr` in virtual parameters. -*The remaining headers are for advanced use*. +*The headers below are for advanced use*. ## Pre-Core Headers diff --git a/doc/modules/ROOT/pages/shared_libraries.adoc b/doc/modules/ROOT/pages/shared_libraries.adoc index 0d5334a..754f7bb 100644 --- a/doc/modules/ROOT/pages/shared_libraries.adoc +++ b/doc/modules/ROOT/pages/shared_libraries.adoc @@ -113,7 +113,7 @@ across modules. ### Indirect Vptrs `initialize` rebuilds the v-tables in the registry. This invalidates all the -`virtual_ptr`{empty}'s, and also the v-table pointers stored in objects by +`virtual_ptr`{empty}s, and also the v-table pointers stored in objects by cpp:inplace_vptr_base[], related to that registry. This is seldom an issue, as most programs that dynamically load shared libraries do so at the very beginning of their execution. @@ -125,7 +125,7 @@ that has the same policies as `default_registry`, plus `indirect_vptr`. We can use it to override the default registry, for example using a compiler command-line switch (`-DBOOST_OPENMETHOD_DEFAULT_REGISTRY=indirect_registry`). -Here is an example of a program that carries `virtual_ptr`{empty}'s across +Here is an example of a program that carries `virtual_ptr`{empty}s across `initialize` calls: [source,c++] diff --git a/doc/modules/ROOT/pages/smart_pointers.adoc b/doc/modules/ROOT/pages/smart_pointers.adoc index c37ed53..074ffec 100644 --- a/doc/modules/ROOT/pages/smart_pointers.adoc +++ b/doc/modules/ROOT/pages/smart_pointers.adoc @@ -2,9 +2,9 @@ ## Smart Pointers If we want maximum performance, we want to use `virtual_ptr`{empty}s in place of -ordinary pointers or references. However, many designs want to use smart -pointers for lifetime management; think of `std::unique_ptr`, `std::shared_ptr`, -or `boost::intrusive_ptr`. +ordinary pointers or references. However, we may also want to use smart pointers +for lifetime management; think of `std::unique_ptr`, `std::shared_ptr`, or +`boost::intrusive_ptr`. Does it mean that we have to choose between the performance of `virtual_ptr` and the convenience of `std::unique_ptr`? Or carry around both types of smart @@ -17,20 +17,18 @@ Node>>` both point to a `const Node`; the former uses a plain `const Node*` while the latter uses a `std::unique_ptr`. Both carry the same v-table pointer. -Smart `virtual_ptr`{empty}'s automatically convert to their non-smart, "plain" +Smart `virtual_ptr`{empty}s automatically convert to their non-smart, "plain" counterparts - e.g. from `virtual_ptr>` 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. 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. -The reverse conversion, from plain to smart, does not exist, because it is -unsafe. - -A smart `virtual_ptr` can be constructed from a corresponding smart pointer, but -not directly from a plain reference or pointer, because it has the potential to -accidentally create smart pointers. +The reverse conversion, from plain to smart, does not exist, because it would +have the potential to accidentally create smart pointers. Likewise, a smart +`virtual_ptr` can be constructed from a smart pointer, but not directly from a +plain reference or pointer. The library provides aliases for standard smart pointers: @@ -39,8 +37,9 @@ The library provides aliases for standard smart pointers: - `shared_virtual_ptr` is an alias for `virtual_ptr>` The standard library provides `std::make_unique` and `std::make_shared` to -create smart pointers. They are convenient, robust, and, in the case of -`std::shared_ptr`, more efficient. OpenMethod provides counterparts: +create smart pointers. They are convenient, robust in presence of exceptions, +and, in the case of `std::shared_ptr`, more efficient. OpenMethod provides +these counterparts: - `make_unique_virtual_ptr(...)` @@ -49,7 +48,16 @@ create smart pointers. They are convenient, robust, and, in the case of Since these functions create the object, they know its exact type with certainty. Thus they don't need to perform a hash table lookup to find the appropriate v-table; they simply read it from a static variable. As a -consequence, they don't even require `Class` to be polymorphic. +consequence, they don't require `Class` to be polymorphic. + +The aliases and the `make_*` functions are aliased in `namespace +boost::openmethod::aliases`, making it convenient to import constructs that are +likely to be used together. + +Smart `virtual_ptr`{empty}s are implemented in their own headers, found in the +`interop` subdirectory. For example, support for `std::unique_ptr` is provided +in ``. `` +does not include smart pointer headers, so they must be included explicitly. Here is a variation of the AST example that uses dynamic allocation and unique pointers: diff --git a/doc/modules/ROOT/pages/virtual_ptr_alt.adoc b/doc/modules/ROOT/pages/virtual_ptr_alt.adoc index b471c67..49d2ed2 100644 --- a/doc/modules/ROOT/pages/virtual_ptr_alt.adoc +++ b/doc/modules/ROOT/pages/virtual_ptr_alt.adoc @@ -70,9 +70,9 @@ jmp qword ptr [rax + 8*rcx] # TAILCALL ### `inplace_vptr` cpp:inplace_vptr_base[] and cpp:inplace_vptr_derived[] automate the creation and -management of embedded vptrs. They are both are +management of embedded vptrs. They are both https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern[CRTP] -classes. +mixin classes. `inplace_vptr_base` is used at the root of a hierarchy. It defines a vptr member variable, and a `boost_openmethod_vptr` friend function that returns its value - @@ -80,6 +80,10 @@ just like in the previous example. `inplace_vptr_derived` is used in derived classes. Their constructors set the vptr member to point to v-table for the class. +WARNING;; `inplace_vptr_derived` must be used at each level of the hierarchy, +and reflect the actual inheritance relationships. Otherwise, the vptrs will not +be set correctly, and the wrong overriders may be called. + [source,c++] ---- include::{example}/3/virtual_ptr_alt.cpp[tag=content] @@ -95,6 +99,5 @@ can be listed after the class being defined. The destructor of `inplace_vptr_derived` set the bases' vptrs back to the v-table for the bases, just like what C++ does for its native vptrs. -WARNING;; `inplace_vptr_derived` must be used at each level of the hierarchy, -and reflect the actual inheritance relationships. Otherwise, the vptrs will not -be set correctly, and the wrong overriders may be called. +`inplace_vptr_base` and `inplace_vptr_derived` are aliased in `namespace +boost::openmethod::aliases`.