lot of doc work, but also rework code

This commit is contained in:
Jean-Louis Leroy
2025-09-28 16:08:11 -04:00
parent 5c3187816d
commit 4fceb9a6fa
79 changed files with 2584 additions and 2066 deletions

View File

@@ -2,8 +2,8 @@ mkdir -p flat/boost/openmethod
python3 dev/flatten.py \
flat/boost/openmethod.hpp \
include/boost/openmethod.hpp \
include/boost/openmethod/unique_ptr.hpp \
include/boost/openmethod/shared_ptr.hpp \
include/boost/openmethod/interop/std_unique_ptr.hpp \
include/boost/openmethod/interop/std_shared_ptr.hpp \
include/boost/openmethod/initialize.hpp
python3 dev/flatten.py \
flat/boost/openmethod/registry.hpp \

View File

@@ -1,31 +0,0 @@
## policy
### Synopsis
Defined in <boost/openmethod/policies/basic_policy.hpp>.
```c++
namespace boost::openmethod::policies {
struct policy {
static auto finalize() -> void;
};
} // boost::openmethod::policies
```
### Description
`policy` is the base class of all policys. It provides an empty `finalize` static
function which can be overriden (via shadowing) by derived classes.
### Members
#### finalize
```c++
static auto finalize() -> void;
```
Does nothing.

View File

@@ -1791,7 +1791,7 @@ pointers:</p>
#include &lt;memory&gt;
#include &lt;boost/openmethod.hpp&gt;
#include &lt;boost/openmethod/unique_ptr.hpp&gt;
#include &lt;boost/openmethod/interop/std_unique_ptr.hpp&gt;
#include &lt;boost/openmethod/compiler.hpp&gt;
using namespace boost::openmethod::aliases;
@@ -2870,7 +2870,7 @@ stored in the same place (the policy&#8217;s <code>static_vptr&lt;Class&gt;</cod
#include &lt;unistd.h&gt;
#include &lt;boost/openmethod.hpp&gt;
#include &lt;boost/openmethod/unique_ptr.hpp&gt;
#include &lt;boost/openmethod/interop/std_unique_ptr.hpp&gt;
#include &lt;boost/openmethod/compiler.hpp&gt;
#include "dl.hpp"
@@ -3117,7 +3117,7 @@ program.</p>
</div>
</div>
<div class="sect4">
<h5 id="ref_boostopenmethodshared_ptr_hpp">&lt;boost/openmethod/shared_ptr.hpp&gt;</h5>
<h5 id="ref_boostopenmethodshared_ptr_hpp">&lt;boost/openmethod/interop/std_shared_ptr.hpp&gt;</h5>
<div class="paragraph">
<p>Provides support for using <code>std::shared_ptr</code> in place of plain pointers in
virtual parameters.</p>
@@ -4211,7 +4211,7 @@ auto operator!=(
</div>
</div>
<div class="paragraph">
<p>Defined in <code>&lt;boost/openmethod/shared_ptr.hpp&gt;</code>:</p>
<p>Defined in <code>&lt;boost/openmethod/interop/std_shared_ptr.hpp&gt;</code>:</p>
</div>
<div class="listingblock">
<div class="content">
@@ -4229,7 +4229,7 @@ inline auto make_shared_virtual(T&amp;&amp;... args)
</div>
</div>
<div class="paragraph">
<p>Defined in <code>&lt;boost/openmethod/unique_ptr.hpp&gt;</code>:</p>
<p>Defined in <code>&lt;boost/openmethod/interop/std_unique_ptr.hpp&gt;</code>:</p>
</div>
<div class="listingblock">
<div class="content">
@@ -4606,13 +4606,13 @@ struct virtual_traits&lt;..., Policy&gt; {
<p><code>T*</code></p>
</li>
<li>
<p><code>std::shared_ptr&lt;T&gt;</code>: defined in &lt;boost/openmethod/shared_ptr.hpp&gt;</p>
<p><code>std::shared_ptr&lt;T&gt;</code>: defined in &lt;boost/openmethod/interop/std_shared_ptr.hpp&gt;</p>
</li>
<li>
<p><code>const std::shared_ptr&lt;T&gt;&amp;</code>: defined in &lt;boost/openmethod/shared_ptr.hpp&gt;</p>
<p><code>const std::shared_ptr&lt;T&gt;&amp;</code>: defined in &lt;boost/openmethod/interop/std_shared_ptr.hpp&gt;</p>
</li>
<li>
<p><code>std::unique_ptr&lt;T&gt;</code>: defined in &lt;boost/openmethod/unique_ptr.hpp&gt;</p>
<p><code>std::unique_ptr&lt;T&gt;</code>: defined in &lt;boost/openmethod/interop/std_unique_ptr.hpp&gt;</p>
</li>
</ul>
</div>

View File

@@ -38,7 +38,7 @@ 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 has more than one base, the `boost_openmethod_vptr` friend
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

View File

@@ -5,111 +5,30 @@
message(STATUS "Building examples")
if (CMAKE_BUILD_TYPE MATCHES "Release")
if (CMAKE_BUILD_TYPE STREQUAL "Release")
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-save-temps -masm=intel)
endif()
endif()
add_executable(virtual_func virtual_func.cpp)
target_link_libraries(virtual_func Boost::openmethod)
add_test(NAME virtual_func COMMAND virtual_func)
add_executable(ast ast.cpp)
target_link_libraries(ast Boost::openmethod)
add_test(NAME ast COMMAND ast)
add_executable(ast_unique_ptr ast_unique_ptr.cpp)
target_link_libraries(ast_unique_ptr Boost::openmethod)
add_test(NAME ast_unique_ptr COMMAND ast_unique_ptr)
add_executable(hello_world hello_world.cpp)
target_link_libraries(hello_world Boost::openmethod)
add_test(NAME hello_world COMMAND hello_world)
add_executable(friendship_all friendship.cpp)
target_compile_definitions(friendship_all PRIVATE FRIEND_ALL)
target_link_libraries(friendship_all Boost::openmethod)
add_test(NAME friendship_add COMMAND friendship_all)
add_executable(friendship friendship.cpp)
target_link_libraries(friendship Boost::openmethod)
add_test(NAME friendship COMMAND friendship)
add_executable(friendship_across_namespaces_all friendship_across_namespaces.cpp)
target_compile_definitions(friendship_across_namespaces_all PRIVATE FRIEND_ALL)
target_link_libraries(friendship_across_namespaces_all Boost::openmethod)
add_test(NAME friendship_across_namespaces_all COMMAND friendship_across_namespaces_all)
add_executable(friendship_across_namespaces friendship_across_namespaces.cpp)
target_link_libraries(friendship_across_namespaces Boost::openmethod)
add_test(NAME friendship_across_namespaces COMMAND friendship_across_namespaces)
add_executable(virtual_ptr virtual_ptr.cpp)
target_link_libraries(virtual_ptr Boost::openmethod)
add_test(NAME virtual_ptr COMMAND virtual_ptr)
add_executable(virtual_ virtual_.cpp)
target_link_libraries(virtual_ Boost::openmethod)
add_test(NAME virtual_ COMMAND virtual_)
add_executable(core_api core_api.cpp)
target_link_libraries(core_api Boost::openmethod)
add_test(NAME core_api COMMAND core_api)
add_executable(default_error_handler default_error_handler.cpp)
target_link_libraries(default_error_handler Boost::openmethod)
add_test(NAME default_error_handler COMMAND default_error_handler)
add_executable(throw_error_handler throw_error_handler.cpp)
target_link_libraries(throw_error_handler Boost::openmethod)
add_test(NAME throw_error_handler COMMAND throw_error_handler)
add_executable(custom_rtti custom_rtti.cpp)
target_link_libraries(custom_rtti Boost::openmethod)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_target_properties(custom_rtti PROPERTIES COMPILE_FLAGS "-fno-rtti")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_definitions(BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS)
endif()
add_test(NAME custom_rtti COMMAND custom_rtti)
add_executable(deferred_custom_rtti deferred_custom_rtti.cpp)
target_link_libraries(deferred_custom_rtti Boost::openmethod)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_target_properties(deferred_custom_rtti PROPERTIES COMPILE_FLAGS "-fno-rtti")
endif()
add_test(NAME deferred_custom_rtti COMMAND deferred_custom_rtti)
file(GLOB cpp_files "*.cpp")
add_executable(slides slides.cpp)
target_link_libraries(slides Boost::openmethod)
add_test(NAME slides COMMAND slides)
foreach(cpp ${cpp_files})
if(cpp MATCHES "dl_.*\\.cpp")
continue()
endif()
add_executable(synopsis synopsis.cpp)
target_link_libraries(synopsis Boost::openmethod)
add_test(NAME synopsis COMMAND synopsis)
add_executable(matrix matrix.cpp)
target_link_libraries(matrix Boost::openmethod)
add_test(NAME matrix COMMAND matrix)
add_executable(accept_no_visitors accept_no_visitors.cpp)
target_link_libraries(accept_no_visitors Boost::openmethod)
add_test(NAME accept_no_visitors COMMAND accept_no_visitors)
add_executable(adventure adventure.cpp)
target_link_libraries(adventure Boost::openmethod)
add_test(NAME adventure COMMAND adventure)
add_executable(next next.cpp)
target_link_libraries(next Boost::openmethod)
add_test(NAME next COMMAND next)
add_executable(asteroids asteroids.cpp)
target_link_libraries(asteroids Boost::openmethod)
add_test(NAME asteroids COMMAND asteroids)
add_executable(static_rtti static_rtti.cpp)
target_link_libraries(static_rtti Boost::openmethod)
add_test(NAME static_rtti COMMAND static_rtti)
cmake_path(REMOVE_EXTENSION cpp LAST_ONLY OUTPUT_VARIABLE stem)
string(REGEX REPLACE ".*/" "" stem ${stem})
add_executable(${stem} ${cpp})
target_link_libraries(${stem} PUBLIC Boost::openmethod Boost::unit_test_framework)
add_test(NAME ${stem} COMMAND ${stem})
add_dependencies(tests ${stem})
endforeach()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Building dlopen example")

View File

@@ -8,7 +8,7 @@
#include <string>
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
using namespace boost::openmethod::aliases;

View File

@@ -11,7 +11,7 @@
#include <memory>
#include <boost/openmethod.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
using namespace boost::openmethod::aliases;

View File

@@ -31,7 +31,7 @@ struct Dog : Animal {
static constexpr unsigned static_type = 3;
};
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <boost/openmethod/policies/vptr_vector.hpp>
// tag::policy[]
@@ -65,8 +65,8 @@ struct custom_rtti : bom::policies::rtti {
// end::policy[]
// tag::registry[]
struct custom_registry : bom::registry<custom_rtti, bom::policies::vptr_vector> {
};
struct custom_registry
: bom::registry<custom_rtti, bom::policies::vptr_vector> {};
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY custom_registry
// end::registry[]

View File

@@ -33,7 +33,7 @@ auto main() -> int {
bom::initialize();
bom::default_registry::error_handler::set([](const auto& error) {
if (std::holds_alternative<bom::not_implemented_error>(error)) {
if (std::holds_alternative<bom::no_overrider>(error)) {
throw std::runtime_error("not implemented");
}
});

View File

@@ -76,7 +76,7 @@ struct Dog : virtual Animal {
static custom_type_info type_info;
};
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <boost/openmethod/policies/vptr_vector.hpp>
namespace bom = boost::openmethod;

View File

@@ -13,7 +13,7 @@
#include <unistd.h>
#include <boost/openmethod.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#include "dl.hpp"
@@ -32,7 +32,7 @@ BOOST_OPENMETHOD_OVERRIDE(
auto main() -> int {
using namespace boost::openmethod;
dynamic::initialize();
initialize<dynamic>();
std::cout << "Before loading library\n";
@@ -61,7 +61,7 @@ auto main() -> int {
std::cout << "\nAfter loading library\n";
dynamic::initialize();
initialize<dynamic>();
auto make_tiger =
reinterpret_cast<Animal* (*)()>(dlsym(handle, "make_tiger"));
@@ -86,7 +86,7 @@ auto main() -> int {
std::cout << "\nAfter unloading library\n";
dynamic::initialize();
initialize<dynamic>();
std::cout << "Gracie encounters Willy -> "
<< encounter(gracie, willy); // ignore

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <iostream>
#include <memory>
#include <boost/openmethod.hpp>
#include <boost/openmethod/inplace_vptr.hpp>
#include <boost/openmethod/initialize.hpp>
using namespace boost::openmethod;
struct Animal : inplace_vptr_base<Animal> {};
struct Cat : Animal, inplace_vptr_derived<Cat, Animal> {};
struct Dog : Animal, inplace_vptr_derived<Dog, Animal> {};
BOOST_OPENMETHOD(
poke, (virtual_<Animal&> animal, std::ostream& os), void);
BOOST_OPENMETHOD_OVERRIDE(poke, (Cat&, std::ostream& os), void) {
os << "hiss\n";
}
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&, std::ostream& os), void) {
os << "bark\n";
}
int main() {
initialize();
std::unique_ptr<Animal> a = std::make_unique<Cat>();
std::unique_ptr<Animal> b = std::make_unique<Dog>();
poke(*a, std::cout); // hiss
poke(*b, std::cout); // bark
return 0;
}

View File

@@ -10,7 +10,7 @@
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
using std::make_shared;
using std::shared_ptr;

View File

@@ -127,7 +127,7 @@ struct Init {
}
#include <boost/openmethod.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
namespace openmethods {

View File

@@ -14,7 +14,7 @@ struct static_registry
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY static_registry
#include <boost/openmethod.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#include <iostream>

View File

@@ -95,7 +95,7 @@ BOOST_OPENMETHOD_OVERRIDE(
auto main() -> int {
// Initialize the dispatch tables.
boost::openmethod::initialize();
boost::openmethod::initialize<>();
// Create a few objects.
// Note that the actual classes are type-erased to base class Animal!

View File

@@ -23,7 +23,7 @@ struct throw_if_not_implemented : bom::policies::error_handler {
static auto error(const bom::openmethod_error&) -> void {
}
static auto error(const bom::not_implemented_error& err) -> void {
static auto error(const bom::no_overrider& err) -> void {
throw err;
}
};
@@ -58,7 +58,7 @@ auto main() -> int {
for (auto animal : animals) {
try {
trick(std::cout, *animal);
} catch (bom::not_implemented_error&) {
} catch (bom::no_overrider&) {
std::cout << "not implemented\n";
}
}

View File

@@ -74,10 +74,10 @@ namespace inplace_vptr {
#include <boost/openmethod/inplace_vptr.hpp>
class Animal : public boost::openmethod::inplace_vptr<Animal> {
class Animal : public boost::openmethod::inplace_vptr_base<Animal> {
};
class Cat : public Animal, public boost::openmethod::inplace_vptr<Cat, Animal> {
class Cat : public Animal, public boost::openmethod::inplace_vptr_derived<Cat, Animal> {
};
BOOST_OPENMETHOD(poke, (std::ostream&, virtual_<Animal&>), void);

View File

@@ -12,5 +12,6 @@
** xref:custom_rtti.adoc[Custom RTTI]
** xref:dynamic_loading.adoc[Dynamic Loading]
* Reference
** xref:headers.adoc[Headers]
** xref:macros.adoc[Macros]
** xref:reference:boost/openmethod.adoc[boost::openmethod]
** xref:reference:boost/openmethod.adoc[Namespace boost::openmethod]

View File

@@ -6,35 +6,38 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
BOOST_OPENMETHOD(NAME, (PARAMETERS...), RETURN_TYPE [, REGISTRY]);
BOOST_OPENMETHOD(ID, (PARAMETERS...), RETURN_TYPE [, REGISTRY]);
```
### Description
Declare a method.
Declares a method.
The macro expands to several constructs:
* A `struct` forward declaration that acts as the method's identifier:
```c++
struct BOOST_OPENMETHOD_ID(NAME);
struct BOOST_OPENMETHOD_ID(ID);
```
* An inline function template, constrained to take the same `PARAMETERS`,
without the `virtual_` decorators, returning a `RETURN_TYPE`. The function
forwards to +
`method<BOOST_OPENMETHOD_ID(NAME)(PARAMETERS...), RETURN_TYPE, REGISTRY>::fn`.
`method<BOOST_OPENMETHOD_ID(ID)(PARAMETERS...), RETURN_TYPE, REGISTRY>::fn`.
* A guide function used to match overriders with the method:
```c++
auto BOOST_OPENMETHOD_ID(NAME)_guide(...)
auto BOOST_OPENMETHOD_ID(ID)_guide(...)
-> ::boost::openmethod::method<
BOOST_OPENMETHOD_ID(NAME)(PARAMETERS...), RETURN_TYPE [, REGISTRY]>;
BOOST_OPENMETHOD_ID(ID)(PARAMETERS...), RETURN_TYPE [, REGISTRY]>;
```
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.
* A xref:BOOST_OPENMETHOD_REGISTER.adoc[registrar] that adds the method to the
registry.
NOTE: `ID` must be an *identifier*. Qualified names are not allowed.
NOTE: The default value for `REGISTRY` is the value of
`BOOST_OPENMETHOD_DEFAULT_REGISTRY` at the point `<boost/openmethod/core.hpp>` is

View File

@@ -19,5 +19,5 @@ NOTE: The default value for `REGISTRY` is the value of
`BOOST_OPENMETHOD_DEFAULT_REGISTRY` when `<boost/openmethod/core.hpp>` is
included. Subsequently changing it has no retroactive effect.
This macro is a wrapper around `use_classes`; see its documentation for more
This macro is a wrapper around cpp:use_classes[]; see its documentation for more
details.

View File

@@ -11,7 +11,7 @@ Defined in <boost/openmethod/macros.hpp>.
### Description
Declare an overrider for a method.
Declares an overrider for a method.
The method is deduced from a call to a method guide function with the
overrider's arguments.

View File

@@ -15,15 +15,22 @@ Defined in `&lt;https://www.github.com/boostorg/openmethod/blob/develop/include/
The name of the default registry.
`BOOST_OPENMETHOD_DEFAULT_REGISTRY` is the default value for the `Registry`
template parameter of `method`, `use_classes`, and other constructs defined in
`<boost/openmethod/core.hpp>`. If it is not defined,
link:reference/boost/openmethod/default_registry.html[`default_registry`] is used.
template parameter of cpp:method[], cpp:use_classes[], cpp:virtual_ptr[], and
all the constructs that take a registry as a template argument.
`BOOST_OPENMETHOD_DEFAULT_REGISTRY` can be defined by a program to change the
default REGISTRY globally. Once `<boost/openmethod/core.hpp>` has been included,
redefining the symbol has no effect. To override the default REGISTRY, proceed as
follows:
default registry globally, *before* including `<boost/openmethod/core.hpp>`. After that, changing its value has no effect, even on other macros.
To override the default registry, proceed as follows:
1. Define a cpp:registry[] class, either from scratch, or by tuning an existing
registry. Include `<boost/openmethod/preamble.hpp>`,
`<boost/openmethod/default_registry.hpp>`, and headers under
`boost/openmethod/policies` as needed.
2. Set `BOOST_OPENMETHOD_DEFAULT_REGISTRY` to the new registry class.
1. Include headers under `boost/openmethod/policies/` as needed.
2. Create a REGISTRY class, and set `BOOST_OPENMETHOD_DEFAULT_REGISTRY`.
3. Include `<boost/openmethod/core.hpp>`.
NOTE;; Use this feature with caution, as it will cause ODR violations if
different translation units define different default registries.

View File

@@ -6,13 +6,13 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(NAME, (PARAMETERS...), RETURN_TYPE)
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(ID, (PARAMETERS...), RETURN_TYPE)
```
### Description
Define the body of an overrider declared with
`BOOST_OPENMETHOD_DECLARE_OVERRIDER`. It should be called in an implementation
file, and followed by a function body.
Defines the body of an overrider declared with
xref:BOOST_OPENMETHOD_DECLARE_OVERRIDER.adoc[BOOST_OPENMETHOD_DECLARE_OVERRIDER].
It should be called in an implementation file, and followed by a function body.
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.
NOTE: `ID` must be an *identifier*. Qualified names are not allowed.

View File

@@ -0,0 +1,12 @@
= xref:macros.adoc[Macro]&nbsp;BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
Enables runtime checks in default_registry.
== Synopsis
May be defined by a program before including
`<boost/openmethod/default_registry.hpp>` to enable runtime checks.
== Description
See cpp:default_registry[] for details.

View File

@@ -1,14 +0,0 @@
## BOOST_OPENMETHOD_GUIDE
### Synopsis
Defined in <boost/openmethod/macros.hpp>.
```c++
#define BOOST_OPENMETHOD_GUIDE(NAME) /* unspecified */
```
### Description
Expands to the name of the guide function used to match overriders to methods.

View File

@@ -8,10 +8,12 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
#define BOOST_OPENMETHOD_ID(NAME) /* unspecified */
#define BOOST_OPENMETHOD_ID(ID) /* unspecified */
```
### Description
Generates a long, obfuscated name from a short name. All the other names
generated by macros are based on this name.
NOTE: `ID` must be an *identifier*. Qualified names are not allowed.

View File

@@ -8,7 +8,7 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
BOOST_OPENMETHOD_INLINE_OVERRIDE(NAME, (PARAMETERS...), RETURN_TYPE) {
BOOST_OPENMETHOD_INLINE_OVERRIDE(ID, (PARAMETERS...), RETURN_TYPE) {
// body
}
```
@@ -17,5 +17,3 @@ BOOST_OPENMETHOD_INLINE_OVERRIDE(NAME, (PARAMETERS...), RETURN_TYPE) {
`BOOST_OPENMETHOD_INLINE_OVERRIDE` performs the same function as
`BOOST_OPENMETHOD_OVERRIDE`, except that the overrider is defined inline.
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.

View File

@@ -8,7 +8,7 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
BOOST_OPENMETHOD_OVERRIDE(NAME, (PARAMETERS...), RETURN_TYPE) {
BOOST_OPENMETHOD_OVERRIDE(ID, (PARAMETERS...), RETURN_TYPE) {
// body
}
```
@@ -23,23 +23,23 @@ overrider's arguments.
The macro creates several entities in the current scope.
* A class template that acts as a container for the overriders of the methods
called `NAME`:
called `ID`:
```c++
template<typename...> BOOST_OPENMETHOD_OVERRIDERS(NAME);
template<typename...> BOOST_OPENMETHOD_OVERRIDERS(ID);
```
* A specialization of the container template for the overrider:
+
--
```c++
struct BOOST_OPENMETHOD_OVERRIDERS(NAME)<RETURN_TYPE(PARAMETERS...)> {
struct BOOST_OPENMETHOD_OVERRIDERS(ID)<RETURN_TYPE(PARAMETERS...)> {
static auto fn(PARAMETERS...) -> RETURN_TYPE;
static auto has_next() -> bool;
template<typename... Args>
static auto next(typename... Args) -> RETURN_TYPE;
};
```
where:
* `fn` is the overrider function.
@@ -48,14 +48,23 @@ where:
* `next(Args... args)` calls the next most specialized overrider via the
pointer stored in the method's `next<fn>` member variable.
--
Finally, the macro starts the definition of the overrider function:
[]
* A xref:BOOST_OPENMETHOD_REGISTER.adoc[registrar] adding the overrider to the
method.
* Finally, the macro starts the definition of the overrider function:
--
```c++
auto BOOST_OPENMETHOD_OVERRIDERS(NAME)<RETURN_TYPE(PARAMETERS...)>::fn(
auto BOOST_OPENMETHOD_OVERRIDERS(ID)<RETURN_TYPE(PARAMETERS...)>::fn(
PARAMETERS...) -> RETURN_TYPE
```
--
The block following the call to the macro is the body of the function.
{empty}
NOTE: `NAME` must be an *identifier*. Qualified names are not allowed.
The `{}` block following the call to the macro is the body of the function.
NOTE: `ID` must be an *identifier*. Qualified names are not allowed.

View File

@@ -6,10 +6,12 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
#define BOOST_OPENMETHOD_OVERRIDER(NAME, (PARAMETERS...), RETURN_TYPE)
#define BOOST_OPENMETHOD_OVERRIDER(ID, (PARAMETERS...), RETURN_TYPE)
```
### Description
Expands to the specialization of the class template that contains the overrider
for with the given name, parameter list and return type.
NOTE: `ID` must be an *identifier*. Qualified names are not allowed.

View File

@@ -6,11 +6,13 @@
Defined in <boost/openmethod/macros.hpp>.
```c++
#define BOOST_OPENMETHOD_OVERRIDERS(NAME) \
BOOST_PP_CAT(BOOST_OPENMETHOD_ID(NAME), _overriders)
#define BOOST_OPENMETHOD_OVERRIDERS(ID) \
BOOST_PP_CAT(BOOST_OPENMETHOD_ID(ID), _overriders)
```
### Description
`BOOST_OPENMETHOD_OVERRIDERS` expands to the name of the class template that
contains the overriders for all the methods with a given name.
NOTE: `ID` must be an *identifier*. Qualified names are not allowed.

View File

@@ -13,4 +13,7 @@ BOOST_OPENMETHOD_REGISTER(TYPE);
### Description
Creates a static instance of `TYPE`, using a unique generated name.
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:
methods and class registrations add themselves to a cpp:registry[], and
overriders add themselves to a method's overrider list.

View File

@@ -0,0 +1,141 @@
[#headers]
= xref:headers.adoc[Headers]
:toc:
{empty}
*NOTE*: The two important headers are:
* xref:#main[`boost/openmethod.hpp`] to define open-methods and overriders using
convenient macros.
* xref:#initialize[`boost/openmethod.hpp/initialize`] to initialize the library.
Typically only included in the translation unit containing `main`.
The following headers make it possible to use standard smart pointers in virtual
parameters:
* xref:#std_shared_ptr[`boost/openmethod/interop/std_shared_ptr.hpp`] to use
`std::shared_ptr` in virtual parameters.
* xref:#std_unique_ptr[`boost/openmethod/interop/std_unique_ptr.hpp`] to use
`std::unique_ptr` in virtual parameters.
*The other headers are for advanced use*.
### boost/openmethod/preamble.hpp
Defines `registry` and stock policies. Also defines all the types and functions
necessary to the definition of `registry`.
Most users should not include this header directly, unless for the purpose of
overriding the default registry.
### boost/openmethod/policies/std_rtti.hpp
Includes all the headers above.
Provides an implementation of the `rtti` policy using standard RTTI.
### boost/openmethod/policies/fast_perfect_hash.hpp
Includes all the headers above.
Provides an implementation of the `hash` policy using a fast perfect hash
function.
### boost/openmethod/policies/vptr_vector.hpp
Includes all the headers above.
Provides an implementation of the `vptr` policy that stores the v-table pointers
in a `std::vector` indexed by type ids, possibly hashed.
### boost/openmethod/policies/default_error_handler.hpp
Includes all the headers above.
Provides an implementation of the `error_handler` policy that calls a
`std::function<void(openmethod_error)>` when an error is encountered, and before
the library aborts the program.
### boost/openmethod/policies/stderr_output.hpp
Includes all the headers above.
Provides an implementation of the `output` policy that writes diagnostics to
the C standard error stream (not using iostreams).
### boost/openmethod/default_registry.hpp
Includes all the headers above.
Defines the default registry, which contains all the stock policies listed
above.
Most users should not include this header directly, unless for the purpose of
overriding the default registry.
### boost/openmethod/core.hpp
Includes all the headers above.
Defines the main constructs of the library: methods, overriders and virtual
pointers, and mechanisms to implement them. Does not define any public macros
apart from `BOOST_OPENMETHOD_DEFAULT_REGISTRY`, if it is not defined already.
Users with a severe allergy to macros can include this header.
### boost/openmethod/macros.hpp
Includes all the headers above.
Defines the public macros of the library, such as `BOOST_OPENMETHOD`,
`BOOST_OPENMETHOD_CLASSES`, etc.
There is little point in including this header directly, as it has the same
effect as including `boost/openmethod.hpp`, which is shorter.
[#main]
### boost/openmethod.hpp
Includes all the headers above.
This is the header that most users should include.
### boost/openmethod/policies/static_rtti.hpp
Includes `preamble.hpp`.
Provides a minimal implementation of the `rtti` policy that does not depend on
standard RTTI.
### boost/openmethod/policies/throw_error_handler.hpp
Includes `preamble.hpp`.
Provides an implementation of the `error_handler` policy that throws errors as
exceptions.
### boost/openmethod/policies/vptr_map.hpp
Includes `preamble.hpp`.
Provides an implementation of the `vptr` policy that stores the v-table pointers
in a map (by default a `std::map`) indexed by type ids.
[#std_shared_ptr]
### boost/openmethod/interop/std_shared_ptr.hpp
Includes `core.hpp`.
Provides a `virtual_traits` specialization that make it possible to use a
`std::shared_ptr` in place of a raw pointer or reference in virtual parameters.
[#std_unique_ptr]
### boost/openmethod/interop/std_unique_ptr.hpp
Includes `core.hpp`.
Provides a `virtual_traits` specialization that make it possible to use a
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.

View File

@@ -5,27 +5,29 @@
[cols=2]
|===
| Name
| Description
| Description.
| xref:BOOST_OPENMETHOD.adoc[BOOST_OPENMETHOD]
| Declare a method
| Declare a method.
| xref:BOOST_OPENMETHOD_CLASSES.adoc[BOOST_OPENMETHOD_CLASSES]
| registers classes
| registers classes.
| xref:BOOST_OPENMETHOD_DECLARE_OVERRIDER.adoc[BOOST_OPENMETHOD_DECLARE_OVERRIDER]
| Declare a method overrider
| xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[BOOST_OPENMETHOD_DEFAULT_REGISTRY]
| Default registry
| Declare a method overrider.
| xref:BOOST_OPENMETHOD_DEFINE_OVERRIDER.adoc[BOOST_OPENMETHOD_DEFINE_OVERRIDER]
| Define the body of a method overrider
| Define the body of a method overrider.
| xref:BOOST_OPENMETHOD_ID.adoc[BOOST_OPENMETHOD_ID]
| Generate a method id
| Generate a method id.
| xref:BOOST_OPENMETHOD_INLINE_OVERRIDE.adoc[BOOST_OPENMETHOD_INLINE_OVERRIDE]
| Add an overrider to a method as an inline function
| Add an overrider to a method as an inline function.
| xref:BOOST_OPENMETHOD_OVERRIDE.adoc[BOOST_OPENMETHOD_OVERRIDE]
| Add an overrider to a method
| Add an overrider to a method.
| xref:BOOST_OPENMETHOD_OVERRIDER.adoc[BOOST_OPENMETHOD_OVERRIDER]
| Return the class template specialization containing an overrider
| Return the class template specialization containing an overrider.
| xref:BOOST_OPENMETHOD_OVERRIDERS.adoc[BOOST_OPENMETHOD_OVERRIDERS]
| Return the class template containing the overriders for all the methods with a given name
| Return the class template containing the overriders for all the methods with a given name.
| xref:BOOST_OPENMETHOD_REGISTER.adoc[BOOST_OPENMETHOD_REGISTER]
| Create a registrar object
| Create a registrar object.
| xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[BOOST_OPENMETHOD_DEFAULT_REGISTRY]
| Default registry.
| xref:BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS.adoc[BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS]
| Enable runtime checks in method calls.
|===

View File

@@ -0,0 +1,5 @@
[#virtual_parameter]
= xref:virtual_parameter.adoc[Macros]
A virtual parameter is a parameter of an open-method that is either a @ref
virtual_ptr, or is decorated with @ref virtual_

View File

@@ -73,7 +73,7 @@ Provides `intialize` and `finalize`. Typically included only by the translation
unit that contains `main`, unless dynamic loading is used in other places in the
program.
#### <boost/openmethod/shared_ptr.hpp>
#### <boost/openmethod/interop/std_shared_ptr.hpp>
Provides support for using `std::shared_ptr` in place of plain pointers in
virtual parameters.

View File

@@ -63,7 +63,7 @@ auto operator!=(
} // namespace boost::openmethod
```
Defined in `<boost/openmethod/shared_ptr.hpp>`:
Defined in `<boost/openmethod/interop/std_shared_ptr.hpp>`:
```c++
namespace boost::openmethod {
@@ -78,7 +78,7 @@ inline auto make_shared_virtual(T&&... args)
}
```
Defined in `<boost/openmethod/unique_ptr.hpp>`:
Defined in `<boost/openmethod/interop/std_unique_ptr.hpp>`:
```c++
namespace boost::openmethod {

View File

@@ -36,9 +36,9 @@ Specializations are provided for:
* `T&`
* `T&&`
* `T*`
* `std::shared_ptr<T>`: defined in <boost/openmethod/shared_ptr.hpp>
* `const std::shared_ptr<T>&`: defined in <boost/openmethod/shared_ptr.hpp>
* `std::unique_ptr<T>`: defined in <boost/openmethod/unique_ptr.hpp>
* `std::shared_ptr<T>`: defined in <boost/openmethod/interop/std_shared_ptr.hpp>
* `const std::shared_ptr<T>&`: defined in <boost/openmethod/interop/std_shared_ptr.hpp>
* `std::unique_ptr<T>`: defined in <boost/openmethod/interop/std_unique_ptr.hpp>
### Members

View File

@@ -20,7 +20,7 @@
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <boost/openmethod/default_registry.hpp>
#ifndef BOOST_OPENMETHOD_DEFAULT_REGISTRY
@@ -35,10 +35,6 @@
#endif
//! Main namespace of the OpenMethod library.
//!
//! @note Names in CamelCase are for exposition only. Blueprints are
//! exposition-only classes that describe the requirements for existing classes.
namespace boost::openmethod {
#ifdef __MRDOCS__
@@ -216,7 +212,9 @@ BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
//! `Cat&`)
//! @li cast the argument to another type (e.g. cast a `Animal&` to a `Cat&`)
//!
//! Specializations of `virtual_traits` must conform to the @ref VirtualTraits
//!
//! @par Requirements
//! Specializations of `virtual_traits` must implement the members deswcribed to the @ref VirtualTraits
//! blueprint.
//!
//! @tparam T A type referring (in the broad sense) to an instance of a class.
@@ -398,13 +396,13 @@ class use_classes {
detail::use_classes_tuple_type<Classes...> tuple;
};
void boost_openmethod_vptr(...);
// =============================================================================
// virtual_ptr
namespace detail {
void boost_openmethod_vptr(...);
template<typename, class, typename = void>
struct is_smart_ptr_aux : std::false_type {};
@@ -458,7 +456,7 @@ constexpr bool IsPolymorphic = Registry::rtti::template is_polymorphic<Class>;
template<typename Class, class Registry>
constexpr bool IsSmartPtr = detail::is_smart_ptr_aux<Class, Registry>::value;
//! Test if arguments are smart pointers of same type (exposition only)
//! Test if arguments are same kind of smart pointers (exposition only)
//!
//! Evaluates to `true` if `Class` and `Other` are both smart pointers of the
//! same type.
@@ -509,7 +507,7 @@ constexpr bool has_vptr_fn = std::is_same_v<
template<class Registry, class ArgType>
decltype(auto) acquire_vptr(const ArgType& arg) {
Registry::check_initialized();
Registry::require_initialized();
if constexpr (detail::has_vptr_fn<ArgType, Registry>) {
return boost_openmethod_vptr(arg, static_cast<Registry*>(nullptr));
@@ -539,23 +537,25 @@ inline vptr_type null_vptr = nullptr;
} // namespace detail
//! Create a `virtual_ptr` for an object of an exact known type
//! Creates a `virtual_ptr` for an object of a known dynamic type.
//!
//! `final_virtual_ptr` creates a `virtual_ptr` to an object, setting its
//! v-table pointer according to the declared type of its argument. It assumes
//! that the static and dynamic types are the same. The v-table pointer is
//! initialized from the `Policy::static_vptr` for the class, which needs
//! not be polymorphic.
//! Creates a @ref virtual_ptr to an object, setting its v-table pointer
//! according to the declared type of its argument. Assumes that the static and
//! dynamic types are the same. Sets the v-table pointer to the
//! @ref registry::static_vptr for the class.
//!
//! @par Errors If the registry has runtime checks enabled, and the argument is
//! a class type that is polymorphic according to the registry's `rtti` policy,
//! a check is performed to verify that the static and dynamic types are indeed
//! the same. If they are not, and if the registry contains an @ref
//! error_handler policy, its
//! @ref error function is called with a @ref final_error value, then the
//! program is terminated with @ref abort.
//! `Class` is _not_ required to be polymorphic.
//!
//! @tparam Registry The registry in which the class is registered.
//! If runtime checks are enabled, and the argument is polymorphic, checks if
//! the static and dynamic types are the same. If not, calls the error handler
//! with a @ref final_error value, then terminates the program with @ref abort.
//!
//! @par Errors
//!
//! @li @ref final_error The static and dynamic types of the object are
//! different.
//!
//! @tparam Registry A @ref registry.
//! @tparam Arg The type of the argument.
//! @param obj A reference to an object.
//! @return A `virtual_ptr<Class, Registry>` pointing to `obj`.
@@ -592,6 +592,12 @@ inline auto final_virtual_ptr(Arg&& obj) {
Registry::template static_vptr<Class>));
}
//! Create a `virtual_ptr` for an object of a known dynamic type.
//!
//! This is an overload of `final_virtual_ptr` that uses the default
//! registry as the `Registry` template parameter.
//!
//! @see @ref final_virtual_ptr
template<class Arg>
inline auto final_virtual_ptr(Arg&& obj) {
return final_virtual_ptr<BOOST_OPENMETHOD_DEFAULT_REGISTRY, Arg>(
@@ -613,16 +619,14 @@ inline auto final_virtual_ptr(Arg&& obj) {
//! "plain" `virtual_ptr` can be constructed from a smart `virtual_ptr`, but not
//! the other way around.
//!
//! TODO: link out from mrdocs to macro documentation
//! The default value for `Registry` can be customized by defining the <a href="openmethod/BOOST_OPENMETHOD_DEFAULT_REGISTRY.html">BOOST_OPENMETHOD_DEFAULT_REGISTRY</a>
//! The default value for `Registry` can be customized by defining the
//! [BOOST_OPENMETHOD_DEFAULT_REGISTRY](../BOOST_OPENMETHOD_DEFAULT_REGISTRY.html)
//! preprocessor symbol.
//!
//! The default value for `Registry` can be customized by defining the
//! xref:BOOST_OPENMETHOD_DEFAULT_REGISTRY.adoc[`BOOST_OPENMETHOD_DEFAULT_REGISTRY`]
//! preprocessor symbol.
//!
//! @par Examples
//!
//! @par Requirements
//!
//! @li @ref virtual_traits must be specialized for `Class&`.
@@ -728,7 +732,7 @@ class virtual_ptr {
//! The following errors may occur, depending on the policies selected in
//! `Registry`:
//!
//! @li @ref unknown_class_error
//! @li @ref missing_class
template<
class Other,
typename = std::enable_if_t<
@@ -778,7 +782,7 @@ class virtual_ptr {
//! The following errors may occur, depending on the policies selected in
//! `Registry`:
//!
//! @li @ref unknown_class_error
//! @li @ref missing_class
template<
class Other,
typename = std::enable_if_t<
@@ -890,7 +894,7 @@ class virtual_ptr {
//! The following errors may occur, depending on the policies selected in
//! `Registry`:
//!
//! @li @ref unknown_class_error
//! @li @ref missing_class
template<
class Other,
typename = std::enable_if_t<
@@ -940,7 +944,7 @@ class virtual_ptr {
//! The following errors may occur, depending on the policies selected in
//! `Registry`:
//!
//! @li @ref unknown_class_error
//! @li @ref missing_class
template<
class Other,
typename = std::enable_if_t<
@@ -1735,16 +1739,40 @@ class virtual_ptr<
}
};
//! Construct a `virtual_ptr` from a lvalue reference.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @param obj A lvalue reference to an object.
//! @return A `virtual_ptr<Class>`.
template<class Class>
virtual_ptr(Class&) -> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
virtual_ptr(Class& obj)
-> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
//! Construct a `virtual_ptr` from a xvalue reference.
//!
//! @tparam Class A class type.
//! @param obj A xvalue reference to an object.
//! @return A `virtual_ptr<Class>`.
template<class Class>
virtual_ptr(Class&&) -> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
virtual_ptr(Class&& obj)
-> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
// Alas this is not allowed:
// template<class Registry, class Class>
// virtual_ptr<Registry>(Class&) -> virtual_ptr<Class, Registry>;
//! Compare two `virtual_ptr`s for equality.
//!
//! Compare the underlying object pointers for equality. The v-table pointers
//! are not compared.
//!!
//! @tparam Left The type of the left-hand side argument.
//! @tparam Right The type of the right-hand side argument.
//! @tparam Registry A @ref registry.
//! @param left A reference to a `virtual_ptr`.
//! @param right A reference to a `virtual_ptr`.
//! @return `true` if both `virtual_ptr`s point to the same object or both
//! are `nullptr`, `false` otherwise.
template<class Left, class Right, class Registry>
auto operator==(
const virtual_ptr<Left, Registry>& left,
@@ -1752,6 +1780,17 @@ auto operator==(
return left.pointer() == right.pointer();
}
//! Compare two `virtual_ptr`s for inequality.
//!
//! Compare the underlying object pointers for inequality. The v-table pointers
//! are not compared.
//!! @tparam Left The type of the left-hand side argument.
//! @tparam Right The type of the right-hand side argument.
//! @tparam Registry A @ref registry.
//! @param left A reference to a `virtual_ptr`.
//! @param right A reference to a `virtual_ptr`.
//! @return `true` if both `virtual_ptr`s point to different objects, or one
//! is `nullptr` and the other is not, `false` otherwise.
template<class Left, class Right, class Registry>
auto operator!=(
const virtual_ptr<Left, Registry>& left,
@@ -1881,9 +1920,9 @@ using overrider_virtual_types = boost::mp11::mp_remove<
void>;
template<class Method, class Rtti, std::size_t Index>
struct init_call_error {
struct init_bad_call {
template<typename Arg, typename... Args>
static auto fn(call_error& error, const Arg& arg, const Args&... args) {
static auto fn(bad_call& error, const Arg& arg, const Args&... args) {
if constexpr (Index == 0u) {
error.method = Rtti::template static_type<Method>();
error.arity = sizeof...(args);
@@ -1899,30 +1938,19 @@ struct init_call_error {
error.types[Index] = arg_type_id;
init_call_error<Method, Rtti, Index + 1>::fn(error, args...);
init_bad_call<Method, Rtti, Index + 1>::fn(error, args...);
}
static auto fn(call_error&) {
static auto fn(bad_call&) {
}
};
template<class Method, class Rtti>
struct init_call_error<Method, Rtti, call_error::max_types> {
static auto fn(call_error&) {
struct init_bad_call<Method, Rtti, bad_call::max_types> {
static auto fn(bad_call&) {
}
};
template<class Method>
struct static_offsets;
template<class Method, typename = void>
struct has_static_offsets : std::false_type {};
template<class Method>
struct has_static_offsets<
Method, std::void_t<decltype(static_offsets<Method>::slots)>>
: std::true_type {};
template<class Registry>
using method_base = std::conditional_t<
Registry::has_deferred_static_rtti, deferred_method_info, method_info>;
@@ -2040,14 +2068,14 @@ struct validate_method_parameter<
//! 1. If `result` is a `virtual_ptr`, get the pointer to the v-table from it.
//!
//! 2. If `boost_openmethod_vptr` can be called with `result` and a `Registry*`,
//! and returns a `vptr_type`, call it.
//! and it returns a `vptr_type`, call it.
//!
//! 3. Call `Registry::rtti::dynamic_vptr(result)`.
//!
//! @par N2216 Handling of Ambiguous Calls
//!
//! If `Registry` contains the @ref n2216 policy, ambiguous calls are not an
//! error. Instead, the following extra steps are taken to select an
//! If `Registry` was initialized with the @ref N2216 option, ambiguous calls
//! are not an error. Instead, the following extra steps are taken to select an
//! overrider:
//!
//! 1. If the return type is a registered polymorphic type, remove all the
@@ -2122,7 +2150,7 @@ class method<Id, ReturnType(Parameters...), Registry>
//! Check if a next most specialized overrider exists
//!
//! Return `true` if a next most specialized overrider after _Fn_ exists,
//! and @ref next can be called without causing a @ref call_error.
//! and @ref next can be called without causing a @ref bad_call.
//!
//! @par Requirements
//!
@@ -2218,10 +2246,6 @@ class method<Id, ReturnType(Parameters...), Registry>
template<typename ArgType>
auto vptr(const ArgType& arg) const -> vptr_type;
template<class Error>
auto
check_static_offset(std::size_t actual, std::size_t expected) const -> void;
template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
auto resolve_uni(const ArgType& arg, const MoreArgTypes&... more_args) const
-> detail::word;
@@ -2334,12 +2358,7 @@ method<Id, ReturnType(Parameters...), Registry>::method() {
this->vp_begin = vp_type_ids;
this->vp_end = vp_type_ids + Arity;
this->not_implemented = reinterpret_cast<void (*)()>(fn_not_implemented);
if constexpr (Registry::has_n2216) {
this->ambiguous = nullptr;
} else {
this->ambiguous = reinterpret_cast<void (*)()>(fn_ambiguous);
}
this->ambiguous = reinterpret_cast<void (*)()>(fn_ambiguous);
Registry::methods.push_back(*this);
}
@@ -2369,26 +2388,6 @@ method<Id, ReturnType(Parameters...), Registry>::~method() {
Registry::methods.remove(*this);
}
template<
typename Id, typename... Parameters, typename ReturnType, class Registry>
template<class Error>
auto method<Id, ReturnType(Parameters...), Registry>::check_static_offset(
std::size_t actual, std::size_t expected) const -> void {
using namespace detail;
if constexpr (Registry::has_error_handler) {
if (actual != expected) {
Error error;
error.method = Registry::rtti::template static_type<method>();
error.expected = this->slots_strides[0];
error.actual = actual;
Registry::error_handler::error(error);
abort();
}
}
}
// -----------------------------------------------------------------------------
// method dispatch
@@ -2414,7 +2413,7 @@ BOOST_FORCEINLINE
const ArgType&... args) const {
using namespace detail;
Registry::check_initialized();
Registry::require_initialized();
void (*pf)();
@@ -2455,16 +2454,7 @@ method<Id, ReturnType(Parameters...), Registry>::resolve_uni(
if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
vptr_type vtbl = vptr<ArgType>(arg);
if constexpr (has_static_offsets<method>::value) {
if constexpr (Registry::has_runtime_checks) {
check_static_offset<static_slot_error>(
static_offsets<method>::slots[0], this->slots_strides[0]);
}
return vtbl[static_offsets<method>::slots[0]];
} else {
return vtbl[this->slots_strides[0]];
}
return vtbl[this->slots_strides[0]];
} else {
return resolve_uni<mp_rest<MethodArgList>>(more_args...);
}
@@ -2483,17 +2473,7 @@ method<Id, ReturnType(Parameters...), Registry>::resolve_multi_first(
if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
vptr_type vtbl = vptr<ArgType>(arg);
std::size_t slot;
if constexpr (has_static_offsets<method>::value) {
slot = static_offsets<method>::slots[0];
if constexpr (Registry::has_runtime_checks) {
check_static_offset<static_slot_error>(
static_offsets<method>::slots[0], this->slots_strides[0]);
}
} else {
slot = this->slots_strides[0];
}
std::size_t slot = this->slots_strides[0];
// The first virtual parameter is special. Since its stride is
// 1, there is no need to store it. Also, the method table
@@ -2523,23 +2503,8 @@ method<Id, ReturnType(Parameters...), Registry>::resolve_multi_next(
if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
vptr_type vtbl = vptr<ArgType>(arg);
std::size_t slot, stride;
if constexpr (has_static_offsets<method>::value) {
slot = static_offsets<method>::slots[VirtualArg];
stride = static_offsets<method>::strides[VirtualArg - 1];
if constexpr (Registry::has_runtime_checks) {
check_static_offset<static_slot_error>(
this->slots_strides[VirtualArg], slot);
check_static_offset<static_stride_error>(
this->slots_strides[2 * VirtualArg], stride);
}
} else {
slot = this->slots_strides[VirtualArg];
stride = this->slots_strides[Arity + VirtualArg - 1];
}
std::size_t slot = this->slots_strides[VirtualArg];
std::size_t stride = this->slots_strides[Arity + VirtualArg - 1];
dispatch = dispatch + vtbl[slot].i * stride;
}
@@ -2564,10 +2529,8 @@ method<Id, ReturnType(Parameters...), Registry>::has_next() -> bool {
return false;
}
if constexpr (!Registry::has_n2216) {
if (next<Fn> == fn_ambiguous) {
return false;
}
if (next<Fn> == fn_ambiguous) {
return false;
}
return true;
@@ -2581,8 +2544,8 @@ method<Id, ReturnType(Parameters...), Registry>::fn_not_implemented(
using namespace policies;
if constexpr (Registry::has_error_handler) {
not_implemented_error error;
detail::init_call_error<method, rtti, 0u>::fn(
no_overrider error;
detail::init_bad_call<method, rtti, 0u>::fn(
error,
detail::parameter_traits<Parameters, Registry>::peek(args)...);
Registry::error_handler::error(error);
@@ -2599,8 +2562,8 @@ method<Id, ReturnType(Parameters...), Registry>::fn_ambiguous(
using namespace policies;
if constexpr (Registry::has_error_handler) {
ambiguous_error error;
detail::init_call_error<method, rtti, 0u>::fn(
ambiguous_call error;
detail::init_bad_call<method, rtti, 0u>::fn(
error,
detail::parameter_traits<Parameters, Registry>::peek(args)...);
Registry::error_handler::error(error);
@@ -2767,7 +2730,7 @@ void method<Id, ReturnType(Parameters...), Registry>::override_impl<
this->vp_type_ids);
}
//! Contains aliases for the most frequently used types in the library
//! Aliases for the most frequently used types in the library.
namespace aliases {
using boost::openmethod::final_virtual_ptr;

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_DEFAULT_REGISTRY_HPP
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <boost/openmethod/policies/std_rtti.hpp>
#include <boost/openmethod/policies/vptr_vector.hpp>
#include <boost/openmethod/policies/stderr_output.hpp>
@@ -15,33 +15,44 @@
namespace boost::openmethod {
struct release_registry
//! Default registry.
//!
//! `default_registry` is a predefined @ref registry, and the default value of
//! [BOOST_OPENMETHOD_DEFAULT_REGISTRY](../BOOST_OPENMETHOD_DEFAULT_REGISTRY.html).
//! It contains the following policies:
//! @li @ref policies::std_rtti: Use standard RTTI.
//! @li @ref policies::fast_perfect_hash: Use a fast perfect hash function to
//! map type ids to indices.
//! @li @ref policies::vptr_vector: Store v-table pointers in a `std::vector`.
//! @li @ref policies::default_error_handler: Write short diagnostic messages.
//! @li @ref policies::stderr_output: Write messages to `stderr`.
//!
//! If
//! [BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS](../BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS.html)
//! is defined, `default_registry` also includes the @ref runtime_checks policy.
//!
//! @note Use `BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS` with caution, as
//! inconsistent use of the macro can cause ODR violations. If defined, it must
//! be in all the translation units in the program that use `default_registry`,
//! including those pulled from libraries.
struct default_registry
: registry<
policies::std_rtti, policies::fast_perfect_hash,
policies::vptr_vector, policies::default_error_handler,
policies::stderr_output> {};
//! Registry with runtime checks and trace enabled
//!
//! `debug_registry` uses the same policies as @ref release_registry, with the
//! additional policies of @ref policies::runtime_checks and @ref
//! policies::trace.
//!
//! This is the default value of
//! [BOOST_OPENMETHOD_DEFAULT_REGISTRY](../BOOST_OPENMETHOD_DEFAULT_REGISTRY.html)
//! when NDEBUG is not defined.
//!
//! `debug_registry` is derived from `release_registry::with<...>`, instead of
//! being aliased, to avoid creating long symbol names wherever it is used. Its
//! state is entirely distinct from `release_registry`\'s.
struct debug_registry
: release_registry::with<policies::runtime_checks, policies::trace> {};
#ifdef NDEBUG
using default_registry = release_registry;
#else
using default_registry = debug_registry;
policies::stderr_output
#ifdef BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
,
policies::runtime_checks
#endif
> {
};
namespace detail {
static odr_check<default_registry> default_registry_odr_check_instance;
}
} // namespace boost::openmethod

View File

@@ -1,155 +0,0 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_OPENMETHOD_DETAIL_TRACE_HPP
#define BOOST_OPENMETHOD_DETAIL_TRACE_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/dynamic_bitset.hpp>
namespace boost::openmethod {
namespace detail {
template<typename Iterator>
struct range;
struct rflush {
std::size_t width;
std::size_t value;
explicit rflush(std::size_t width, std::size_t value)
: width(width), value(value) {
}
};
struct type_name {
type_name(type_id type) : type(type) {
}
type_id type;
};
template<class Registry>
struct trace_type {
std::size_t indentation_level{0};
auto operator++() -> trace_type& {
if constexpr (Registry::has_trace) {
if (Registry::trace::on) {
for (std::size_t i = 0; i < indentation_level; ++i) {
Registry::output::os << " ";
}
}
}
return *this;
}
struct indent {
trace_type& trace;
int by;
explicit indent(trace_type& trace, int by = 2) : trace(trace), by(by) {
trace.indentation_level += by;
}
~indent() {
trace.indentation_level -= by;
}
};
};
template<class Registry, typename T, typename F>
auto write_range(trace_type<Registry>& trace, range<T> range, F fn) -> auto& {
if constexpr (Registry::has_trace) {
if (Registry::trace::on) {
trace << "(";
const char* sep = "";
for (auto value : range) {
trace << sep << fn(value);
sep = ", ";
}
trace << ")";
}
}
return trace;
}
template<class Registry, typename T>
auto operator<<(trace_type<Registry>& trace, const T& value) -> auto& {
if constexpr (Registry::has_trace) {
if (Registry::trace::on) {
Registry::output::os << value;
}
}
return trace;
}
template<class Registry>
auto operator<<(trace_type<Registry>& trace, const rflush& rf) -> auto& {
if constexpr (Registry::has_trace) {
if (Registry::trace::on) {
std::size_t digits = 1;
auto tmp = rf.value / 10;
while (tmp) {
++digits;
tmp /= 10;
}
while (digits < rf.width) {
trace << " ";
++digits;
}
trace << rf.value;
}
}
return trace;
}
template<class Registry>
auto operator<<(
trace_type<Registry>& trace, const boost::dynamic_bitset<>& bits) -> auto& {
if constexpr (Registry::has_trace) {
if (Registry::trace::on) {
auto i = bits.size();
while (i != 0) {
--i;
Registry::output::os << bits[i];
}
}
}
return trace;
}
template<class Registry>
auto operator<<(trace_type<Registry>& trace, const range<type_id*>& tips)
-> auto& {
return write_range(trace, tips, [](auto tip) { return type_name(tip); });
}
template<class Registry, typename T>
auto operator<<(trace_type<Registry>& trace, const range<T>& range) -> auto& {
return write_range(trace, range, [](auto value) { return value; });
}
template<class Registry>
auto operator<<(trace_type<Registry>& trace, const type_name& manip) -> auto& {
if constexpr (Registry::has_trace) {
Registry::rtti::type_name(manip.type, trace);
}
return trace;
}
} // namespace detail
} // namespace boost::openmethod
#endif // BOOST_OPENMETHOD_DETAIL_HPP

View File

@@ -1,281 +0,0 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_OPENMETHOD_DETAIL_TYPES_HPP
#define BOOST_OPENMETHOD_DETAIL_TYPES_HPP
#include <cstdint>
#include <boost/openmethod/detail/static_list.hpp>
#if BOOST_CXX_VERSION >= 202002L
#define BOOST_OPENMETHOD_DETAIL_CXX17(...)
#define BOOST_OPENMETHOD_DETAIL_CXX20(...) __VA_ARGS__
#else
#define BOOST_OPENMETHOD_DETAIL_CXX17(...) __VA_ARGS__
#define BOOST_OPENMETHOD_DETAIL_CXX20(...)
#endif
namespace boost::openmethod {
namespace detail {
union word {
word() {
} // undefined
word(void (*pf)()) : pf(pf) {
}
word(word* pw) : pw(pw) {
}
word(std::size_t i) : i(i) {
}
void (*pf)();
std::size_t i;
word* pw;
};
#if defined(UINTPTR_MAX)
using uintptr = std::uintptr_t;
constexpr uintptr uintptr_max = UINTPTR_MAX;
#else
static_assert(
sizeof(std::size_t) == sizeof(void*),
"This implementation requires that size_t and void* have the same size.");
using uintptr = std::size_t;
constexpr uintptr uintptr_max = (std::numeric_limits<std::size_t>::max)();
#endif
} // namespace detail
using vptr_type = const detail::word*;
using type_id = const void*;
template<typename T>
struct virtual_;
template<typename T, class Registry>
struct virtual_traits;
// -----------------------------------------------------------------------------
// Error handling
struct openmethod_error {};
//! Registry not initialized
struct not_initialized_error : openmethod_error {
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void {
os << "not initialized";
}
};
//! Unknown class
struct unknown_class_error : openmethod_error {
//! The type_id of the unknown class.
type_id type;
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void;
};
//! Cannot find hash factors
struct fast_perfect_hash_error : openmethod_error {
//! Number of attempts to find hash factors
std::size_t attempts;
//! Number of buckets used in the last attempt
std::size_t buckets;
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void;
};
//! No valid overrider
struct call_error : openmethod_error {
//! The type_id of method that was called
type_id method;
//! The number of @em virtual arguments in the call
std::size_t arity;
//! The maximum size of `types`
static constexpr std::size_t max_types = 16;
//! The type_ids of the arguments.
type_id types[max_types];
protected:
template<class Registry, class Stream>
auto write_aux(Stream& os, const char* subtype) const -> void;
};
//! No overrider for virtual tuple
//!
//! @see @ref call_error for data members.
struct not_implemented_error : call_error {
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void {
write_aux<Registry>(os, "not implemented");
}
};
//! Ambiguous call
//!
//! @see @ref call_error for data members.
struct ambiguous_error : call_error {
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void {
write_aux<Registry>(os, "ambiguous");
}
};
//! Static and dynamic type mismatch in "final" construct
//!
//! If runtime checks are enabled, the "final" construct checks that the static
//! and dynamic types of the object, as reported by the `rtti` policy, are the
//! same. If they are not, and if the registry contains an @ref error_handler
//! policy, its @ref error function is called with a `final_error` object, then
//! the program is terminated with
//! @ref abort.
struct final_error : openmethod_error {
type_id static_type, dynamic_type;
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void;
};
//! For future use
struct static_offset_error : openmethod_error {
type_id method;
int actual, expected;
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void;
};
//! For future use
struct static_slot_error : static_offset_error {};
//! For future use
struct static_stride_error : static_offset_error {};
namespace detail {
struct empty {};
template<typename Iterator>
struct range {
range(Iterator first, Iterator last) : first(first), last(last) {
}
Iterator first, last;
auto begin() const -> Iterator {
return first;
}
auto end() const -> Iterator {
return last;
}
};
// -----------------------------------------------------------------------------
// class info
struct class_info : static_list<class_info>::static_link {
type_id type;
vptr_type* static_vptr;
type_id *first_base, *last_base;
bool is_abstract{false};
auto vptr() const {
return static_vptr;
}
auto type_id_begin() const {
return &type;
}
auto type_id_end() const {
return &type + 1;
}
};
struct deferred_class_info : class_info {
virtual void resolve_type_ids() = 0;
};
// -----------
// method info
struct overrider_info;
struct method_info : static_list<method_info>::static_link {
type_id* vp_begin;
type_id* vp_end;
static_list<overrider_info> specs;
void (*not_implemented)();
void (*ambiguous)();
type_id method_type_id;
type_id return_type_id;
std::size_t* slots_strides_ptr;
auto arity() const {
return std::distance(vp_begin, vp_end);
}
};
struct deferred_method_info : method_info {
virtual void resolve_type_ids() = 0;
};
struct overrider_info : static_list<overrider_info>::static_link {
~overrider_info() {
method->specs.remove(*this);
}
method_info* method; // for the destructor, to remove definition
type_id return_type; // for N2216 disambiguation
type_id type; // of the function, for trace
void (**next)();
type_id *vp_begin, *vp_end;
void (*pf)();
};
struct deferred_overrider_info : overrider_info {
virtual void resolve_type_ids() = 0;
};
} // namespace detail
} // namespace boost::openmethod
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -21,18 +21,18 @@ template<class>
struct update_vptr_bases;
template<class To, class Class>
void update_vptr(Class* obj);
void boost_openmethod_update_vptr(Class* obj);
template<class... Bases>
struct update_vptr_bases<mp11::mp_list<Bases...>> {
template<class To, class Class>
static void fn(Class* obj) {
(update_vptr<To>(static_cast<Bases*>(obj)), ...);
(boost_openmethod_update_vptr<To>(static_cast<Bases*>(obj)), ...);
}
};
template<class To, class Class>
void update_vptr(Class* obj) {
void boost_openmethod_update_vptr(Class* obj) {
using registry = inplace_vptr_registry<Class>;
using bases = decltype(boost_openmethod_bases(obj));
@@ -47,11 +47,6 @@ void update_vptr(Class* obj) {
}
}
struct inplace_vptr_derived {};
template<class, class, bool>
class inplace_vptr_aux;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif
@@ -59,25 +54,87 @@ class inplace_vptr_aux;
template<class... Classes>
inline use_classes<Classes...> inplace_vptr_use_classes;
template<class Class, class Registry>
class inplace_vptr_aux<Class, Registry, true> {
protected:
class inplace_vptr_base_tag {};
} // namespace detail
//! Embed a v-table pointer in a class.
//!
//! `inplace_vptr_base` is a [mixin](https://en.wikipedia.org/wiki/Mixin) that
//! embeds a v-table pointer at the root of a class hierarchy. It also declares
//! a @ref boost_openmethod_vptr free function that returns the v-table pointer
//! stored in the object.
//!
//! `inplace_vptr_base` registers the class in `Registry`. It is not necessary
//! to register the class with @ref use_class or
//! [BOOST_OPENMETHOD_REGISTER](../BOOST_OPENMETHOD_REGISTER.html)
//!
//! The v-table pointer is obtained directly from the `Registry`\'s @ref
//! static_vptr variable. No hashing is involved. If all the classes in
//! `Registry` inherit from `inplace_vptr_base`, the registry needs not have a
//! @ref policies::vptr policy, nor any policy it depends on (like @ref
//! policies::type_hash).
//!
//! If `Registry` contains the @ref has_indirect_vptr policy, the v-table
//! pointer is stored as a pointer to a pointer, and remains valid after a call
//! to @ref initialize.
//!
//! The default value of `Registry` can be changed by defining
//! [BOOST_OPENMETHOD_DEFAULT_REGISTRY](../BOOST_OPENMETHOD_DEFAULT_REGISTRY.html)
//!
//! @tparam Class The class in which to embed the v-table pointer.
//! @tparam Registry The @ref registry in which `Class` and its derived classes
//! are registered.
//!
//! @par Example
//! @code
//! #include <boost/openmethod.hpp>
//! #include <boost/openmethod/inplace_vptr.hpp>
//! #include <boost/openmethod/initialize.hpp>
//!
//! using namespace boost::openmethod;
//!
//! struct Animal : inplace_vptr_base<Animal> {};
//!
//! struct Cat : Animal, inplace_vptr_derived<Cat, Animal> {};
//!
//! struct Dog : Animal, inplace_vptr_derived<Dog, Animal> {};
//!
//! BOOST_OPENMETHOD(
//! poke, (virtual_<Animal&> animal, std::ostream& os), void);
//!
//! BOOST_OPENMETHOD_OVERRIDE(poke, (Cat&, std::ostream& os), void) {
//! os << "hiss\n";
//! }
//!
//! BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&, std::ostream& os), void) {
//! os << "bark\n";
//! }
//!
//! int main() {
//! initialize();
//!
//! std::unique_ptr<Animal> a = std::make_unique<Cat>();
//! std::unique_ptr<Animal> b = std::make_unique<Dog>();
//!
//! poke(*a, std::cout); // hiss
//! poke(*b, std::cout); // bark
//!
//! return 0;
//! }
//! @endcode
template<class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
class inplace_vptr_base : protected detail::inplace_vptr_base_tag {
template<class To, class Other>
friend void update_vptr(Other*);
friend void detail::boost_openmethod_update_vptr(Other*);
friend auto boost_openmethod_registry(Class*) -> Registry;
friend auto boost_openmethod_bases(Class*) -> mp11::mp_list<>;
inplace_vptr_aux() {
(void)&inplace_vptr_use_classes<Class, Registry>;
detail::update_vptr<Class>(static_cast<Class*>(this));
}
~inplace_vptr_aux() {
std::conditional_t<Registry::has_indirect_vptr, const vptr_type*, vptr_type>
boost_openmethod_vptr = nullptr;
}
friend auto
boost_openmethod_vptr(const Class& obj, Registry*) -> vptr_type {
boost_openmethod_vptr(const Class& obj, Registry*) noexcept -> vptr_type {
if constexpr (Registry::has_indirect_vptr) {
return *obj.boost_openmethod_vptr;
} else {
@@ -85,69 +142,103 @@ class inplace_vptr_aux<Class, Registry, true> {
}
}
friend auto boost_openmethod_registry(Class*) -> Registry;
std::conditional_t<Registry::has_indirect_vptr, const vptr_type*, vptr_type>
boost_openmethod_vptr = nullptr;
};
template<class Class, class Base>
class inplace_vptr_aux<Class, Base, false> : inplace_vptr_derived {
protected:
friend void update_vptr(Class*);
inplace_vptr_aux() {
(void)&inplace_vptr_use_classes<
Class, Base, inplace_vptr_registry<Class>>;
detail::update_vptr<Class>(static_cast<Class*>(this));
//! Set the vptr to `Class`\'s v-table.
inplace_vptr_base() noexcept {
(void)&detail::inplace_vptr_use_classes<Class, Registry>;
detail::boost_openmethod_update_vptr<Class>(static_cast<Class*>(this));
}
~inplace_vptr_aux() {
detail::update_vptr<Base>(
//! Set the vptr to `nullptr`.
~inplace_vptr_base() noexcept {
boost_openmethod_vptr = nullptr;
}
};
#ifdef __MRDOCS__
//! Adjust the v-table pointer embedded in a class.
//!
//! `inplace_vptr_derived` is a [mixin](https://en.wikipedia.org/wiki/Mixin)
//! that adjusts the v-table pointer in a @ref inplace_vptr_base. It can be used
//! only with classes that have @ref inplace_vptr_base as a direct or indirect
//! base class.
//!
//! `inplace_vptr_derived` registers the class and its bases in `Registry`. It
//! is not necessary to register them with @ref use_class or
//! [BOOST_OPENMETHOD_REGISTER](../BOOST_OPENMETHOD_REGISTER.html)
//!
//! The v-table pointer is obtained directly from the `Registry`\'s @ref
//! static_vptr variable. No hashing is involved. If all the classes in
//! `Registry` inherit from `inplace_vptr_derived`, the registry needs not have a
//! @ref policies::vptr policy, nor any policy it depends on (like @ref
//! policies::type_hash).
//!
//! @see @ref inplace_vptr_base for an example.
//!
//! @tparam Class The class in which to embed the v-table pointer.
//! @tparam Base A direct base class of `Class`.
//! @tparam MoreBases More direct base classes of `Class`.
template<class Class, class Base, class... MoreBases>
class inplace_vptr_derived {
protected:
//! Set the vptr to `Class`\'s v-table.
inplace_vptr_derived() noexcept;
//! Set the vptr in each base class.
//!
//! For each base, set its vptr to the base's v-table.
~inplace_vptr_derived() noexcept;
};
#else
template<class Class, class Base, class... MoreBases>
class inplace_vptr_derived;
#endif
//! Specialization for a single base class.
//!
//!
//! @see The main template for documentation.
template<class Class, class Base>
class inplace_vptr_derived<Class, Base> {
static_assert(
!detail::is_registry<Base>,
"registry can be specified only for root classes");
static_assert(
std::is_base_of_v<detail::inplace_vptr_base_tag, Base>,
"class must inherit from inplace_vptr_base");
template<class To, class Other>
friend void detail::boost_openmethod_update_vptr(Other*);
friend auto boost_openmethod_bases(Class*) -> mp11::mp_list<Base>;
protected:
//! Set the vptr to `Class`\'s v-table.
inplace_vptr_derived() noexcept {
using namespace detail;
(void)&detail::inplace_vptr_use_classes<
Class, Base, detail::inplace_vptr_registry<Class>>;
boost_openmethod_update_vptr<Class>(static_cast<Class*>(this));
}
//! Set the vptr in base class.
~inplace_vptr_derived() noexcept {
detail::boost_openmethod_update_vptr<Base>(
static_cast<Base*>(static_cast<Class*>(this)));
}
friend auto boost_openmethod_bases(Class*) -> mp11::mp_list<Base>;
};
} // namespace detail
template<typename...>
struct inplace_vptr;
template<class Class>
struct inplace_vptr<Class>
: detail::inplace_vptr_aux<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY, true> {
};
template<class Class, class Other>
struct inplace_vptr<Class, Other>
: detail::inplace_vptr_aux<Class, Other, detail::is_registry<Other>> {};
//! Specialization for multiple base classes.
//!
//! @see The main template for documentation.
template<class Class, class Base1, class Base2, class... MoreBases>
struct inplace_vptr<Class, Base1, Base2, MoreBases...>
: detail::inplace_vptr_derived {
class inplace_vptr_derived<Class, Base1, Base2, MoreBases...> {
static_assert(
!detail::is_registry<Base1> && !detail::is_registry<Base2> &&
(!detail::is_registry<MoreBases> && ...),
"registry can be specified only for root classes");
protected:
inplace_vptr() {
(void)&detail::inplace_vptr_use_classes<
Class, Base1, Base2, MoreBases...,
detail::inplace_vptr_registry<Base1>>;
detail::update_vptr<Class>(static_cast<Class*>(this));
}
~inplace_vptr() {
auto obj = static_cast<Class*>(this);
detail::update_vptr<Base1>(static_cast<Base1*>(obj));
detail::update_vptr<Base2>(static_cast<Base2*>(obj));
(detail::update_vptr<MoreBases>(static_cast<Base2*>(obj)), ...);
}
friend auto
boost_openmethod_registry(Class*) -> detail::inplace_vptr_registry<Base1>;
friend auto
@@ -157,8 +248,34 @@ struct inplace_vptr<Class, Base1, Base2, MoreBases...>
detail::inplace_vptr_registry<Base1>* registry) -> vptr_type {
return boost_openmethod_vptr(static_cast<const Base1&>(obj), registry);
}
protected:
//! Set the vptr to `Class`\'s v-table.
inplace_vptr_derived() noexcept {
(void)&detail::inplace_vptr_use_classes<
Class, Base1, Base2, MoreBases...,
detail::inplace_vptr_registry<Base1>>;
detail::boost_openmethod_update_vptr<Class>(static_cast<Class*>(this));
}
//! Set the vptr in each base class.
//!
//! For each base, set its vptr to the base's v-table.
~inplace_vptr_derived() noexcept {
auto obj = static_cast<Class*>(this);
detail::boost_openmethod_update_vptr<Base1>(static_cast<Base1*>(obj));
detail::boost_openmethod_update_vptr<Base2>(static_cast<Base2*>(obj));
(detail::boost_openmethod_update_vptr<MoreBases>(
static_cast<MoreBases*>(obj)),
...);
}
};
namespace aliases {
using boost::openmethod::inplace_vptr_base;
using boost::openmethod::inplace_vptr_derived;
} // namespace aliases
} // namespace boost::openmethod
#endif // BOOST_OPENMETHOD_inplace_vptr_HPP

View File

@@ -3,8 +3,8 @@
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_OPENMETHOD_SHARED_PTR_HPP
#define BOOST_OPENMETHOD_SHARED_PTR_HPP
#ifndef BOOST_OPENMETHOD_INTEROP_SHARED_PTR_HPP
#define BOOST_OPENMETHOD_INTEROP_SHARED_PTR_HPP
#include <boost/openmethod/core.hpp>
#include <memory>

View File

@@ -3,8 +3,8 @@
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_OPENMETHOD_UNIQUE_PTR_HPP
#define BOOST_OPENMETHOD_UNIQUE_PTR_HPP
#ifndef BOOST_OPENMETHOD_INTEROP_UNIQUE_PTR_HPP
#define BOOST_OPENMETHOD_INTEROP_UNIQUE_PTR_HPP
#include <boost/openmethod/core.hpp>

View File

@@ -119,12 +119,7 @@ struct va_args<ReturnType> {
BOOST_ASSERT(has_next()); \
return boost_openmethod_detail_locate_method_aux< \
void ARGS>::type::next<fn>(std::forward<Args>(args)...); \
} \
inline BOOST_OPENMETHOD_REGISTER( \
BOOST_OPENMETHOD_OVERRIDERS(NAME) < __VA_ARGS__ ARGS > \
::boost_openmethod_detail_locate_method_aux<void ARGS>::type:: \
override< \
BOOST_OPENMETHOD_OVERRIDERS(NAME) < __VA_ARGS__ ARGS>::fn >);
}
#define BOOST_OPENMETHOD_OVERRIDER(NAME, ARGS, ...) \
BOOST_OPENMETHOD_OVERRIDERS(NAME)<__VA_ARGS__ ARGS>
@@ -135,10 +130,20 @@ struct va_args<ReturnType> {
#define BOOST_OPENMETHOD_INLINE_OVERRIDE(NAME, ARGS, ...) \
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
inline BOOST_OPENMETHOD_REGISTER( \
BOOST_OPENMETHOD_OVERRIDERS(NAME) < __VA_ARGS__ ARGS > \
::boost_openmethod_detail_locate_method_aux<void ARGS>::type:: \
override< \
BOOST_OPENMETHOD_OVERRIDERS(NAME) < __VA_ARGS__ ARGS>::fn >); \
inline BOOST_OPENMETHOD_DEFINE_OVERRIDER(NAME, ARGS, __VA_ARGS__)
#define BOOST_OPENMETHOD_OVERRIDE(NAME, ARGS, ...) \
BOOST_OPENMETHOD_DECLARE_OVERRIDER(NAME, ARGS, __VA_ARGS__) \
BOOST_OPENMETHOD_REGISTER( \
BOOST_OPENMETHOD_OVERRIDERS(NAME) < __VA_ARGS__ ARGS > \
::boost_openmethod_detail_locate_method_aux<void ARGS>::type:: \
override< \
BOOST_OPENMETHOD_OVERRIDERS(NAME) < __VA_ARGS__ ARGS>::fn >); \
BOOST_OPENMETHOD_DEFINE_OVERRIDER(NAME, ARGS, __VA_ARGS__)
#define BOOST_OPENMETHOD_CLASSES(...) \

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_VECTORED_ERROR_HPP
#define BOOST_OPENMETHOD_POLICY_VECTORED_ERROR_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <functional>
#include <variant>
@@ -32,22 +32,51 @@ namespace boost::openmethod::policies {
//! enable exception throwing on a registry basis.
struct default_error_handler : error_handler {
//! A @ref std::variant containing an instance of a subclass of @ref
//! openmethod_error.
using error_variant = std::variant<
not_initialized_error, not_implemented_error, ambiguous_error,
unknown_class_error, fast_perfect_hash_error, final_error,
static_slot_error, static_stride_error>;
//! The type of the error handler function object.
using function_type = std::function<void(const error_variant& error)>;
//! A model of @ref error_handler::fn.
//!
//! @tparam Registry The registry containing this policy.
template<class Registry>
class fn {
template<typename, typename, typename>
struct error_variant_aux;
template<
typename T, class... Errors, class Policy, class... MorePolicies>
struct error_variant_aux<
T, std::variant<Errors...>, mp11::mp_list<Policy, MorePolicies...>>
: error_variant_aux<
void, std::variant<Errors...>,
mp11::mp_list<MorePolicies...>> {};
template<class... Errors, class Policy, class... MorePolicies>
struct error_variant_aux<
std::void_t<typename Policy::errors>, std::variant<Errors...>,
mp11::mp_list<Policy, MorePolicies...>>
: error_variant_aux<
void,
mp11::mp_append<
std::variant<Errors...>, typename Policy::errors>,
mp11::mp_list<MorePolicies...>> {};
template<class... Errors>
struct error_variant_aux<
void, std::variant<Errors...>, mp11::mp_list<>> {
using type = std::variant<Errors...>;
};
public:
//! A @ref std::variant containing an instance of a subclass of @ref
//! openmethod_error.
using error_variant = typename error_variant_aux<
void,
std::variant<
not_initialized, no_overrider, ambiguous_call,
missing_class, missing_base, odr_violation, final_error>,
typename Registry::policy_list>::type;
//! The type of the error handler function object.
using function_type = std::function<void(const error_variant& error)>;
//! Calls a function with the error object, wrapped in an @ref
//! error_variant.
//!
@@ -96,7 +125,7 @@ struct default_error_handler : error_handler {
};
template<class Registry>
typename default_error_handler::function_type
typename default_error_handler::fn<Registry>::function_type
default_error_handler::fn<Registry>::handler = default_handler;
} // namespace boost::openmethod::policies

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_FAST_PERFECT_HASH_HPP
#define BOOST_OPENMETHOD_POLICY_FAST_PERFECT_HASH_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <limits>
#include <random>
@@ -19,10 +19,21 @@ namespace boost::openmethod {
namespace detail {
#if defined(UINTPTR_MAX)
using uintptr = std::uintptr_t;
constexpr uintptr uintptr_max = UINTPTR_MAX;
#else
static_assert(
sizeof(std::size_t) == sizeof(void*),
"This implementation requires that size_t and void* have the same size.");
using uintptr = std::size_t;
constexpr uintptr uintptr_max = (std::numeric_limits<std::size_t>::max)();
#endif
template<class Registry>
std::vector<type_id> fast_perfect_hash_control;
}
} // namespace detail
namespace policies {
@@ -39,6 +50,24 @@ namespace policies {
//! range of integers. In other words, a lot of space may be wasted in presence
//! of large sets of type_ids.
struct fast_perfect_hash : type_hash {
//! Cannot find hash factors
struct search_error : openmethod_error {
//! Number of attempts to find hash factors
std::size_t attempts;
//! Number of buckets used in the last attempt
std::size_t buckets;
//! Write a short description to an output stream
//! @param os The output stream
//! @tparam Registry The registry
//! @tparam Stream A @ref LightweightOutputStream
template<class Registry, class Stream>
auto write(Stream& os) const -> void;
};
using errors = std::variant<search_error>;
//! A model of @ref type_hash::fn.
//!
//! @tparam Registry The registry containing this policy
@@ -50,7 +79,7 @@ struct fast_perfect_hash : type_hash {
inline static std::size_t max_value;
inline static void check(std::size_t index, type_id type);
template<typename ForwardIterator>
template<class ForwardIterator, class... Options>
static void initialize(
ForwardIterator first, ForwardIterator last,
std::vector<type_id>& buckets);
@@ -69,7 +98,7 @@ struct fast_perfect_hash : type_hash {
//! @ref IdsToVptr objects
//! @param first Beginning of the range
//! @param last End of the range
template<typename ForwardIterator>
template<class ForwardIterator, class... Options>
static auto initialize(ForwardIterator first, ForwardIterator last) {
if constexpr (Registry::has_runtime_checks) {
initialize(
@@ -90,7 +119,7 @@ struct fast_perfect_hash : type_hash {
//! the type id is valid, i.e. if it was present in the set passed to
//! @ref initialize. Its absence indicates that a class involved in a
//! method definition, method overrider, or method call was not
//! registered. In this case, signal a @ref unknown_class_error using
//! registered. In this case, signal a @ref missing_class using
//! the registry's @ref error_handler if present; then calls `abort`.
//!
//! @param type The type_id to hash
@@ -115,7 +144,7 @@ struct fast_perfect_hash : type_hash {
};
template<class Registry>
template<typename ForwardIterator>
template<class ForwardIterator, class... Options>
void fast_perfect_hash::fn<Registry>::initialize(
ForwardIterator first, ForwardIterator last,
std::vector<type_id>& buckets) {
@@ -123,8 +152,10 @@ void fast_perfect_hash::fn<Registry>::initialize(
const auto N = std::distance(first, last);
if constexpr (Registry::has_trace && Registry::has_output) {
if (Registry::trace::on) {
if constexpr (
mp11::mp_contains<mp11::mp_list<Options...>, trace>::value &&
Registry::has_output) {
if (trace::on) {
Registry::output::os << "Finding hash factor for " << N
<< " types\n";
}
@@ -146,7 +177,9 @@ void fast_perfect_hash::fn<Registry>::initialize(
min_value = (std::numeric_limits<std::size_t>::max)();
max_value = (std::numeric_limits<std::size_t>::min)();
if constexpr (Registry::has_trace && Registry::has_output) {
if constexpr (
mp11::mp_contains<mp11::mp_list<Options...>, trace>::value &&
Registry::has_output) {
if (Registry::trace::on) {
Registry::output::os << " trying with M = " << M << ", "
<< hash_size << " buckets\n";
@@ -180,7 +213,9 @@ void fast_perfect_hash::fn<Registry>::initialize(
}
}
if constexpr (Registry::has_trace && Registry::has_output) {
if constexpr (
mp11::mp_contains<mp11::mp_list<Options...>, trace>::value &&
Registry::has_output) {
if (Registry::trace::on) {
Registry::output::os
<< " found " << mult << " after " << total_attempts
@@ -195,7 +230,7 @@ void fast_perfect_hash::fn<Registry>::initialize(
}
}
fast_perfect_hash_error error;
search_error error;
error.attempts = total_attempts;
error.buckets = std::size_t(1) << M;
@@ -212,7 +247,7 @@ void fast_perfect_hash::fn<Registry>::check(std::size_t index, type_id type) {
detail::fast_perfect_hash_control<Registry>[index] != type) {
if constexpr (Registry::has_error_handler) {
unknown_class_error error;
missing_class error;
error.type = type;
Registry::error_handler::error(error);
}
@@ -221,6 +256,12 @@ void fast_perfect_hash::fn<Registry>::check(std::size_t index, type_id type) {
}
}
template<class Registry, class Stream>
auto fast_perfect_hash::search_error::write(Stream& os) const -> void {
os << "could not find hash factors after " << attempts << "s using "
<< buckets << " buckets\n";
}
} // namespace policies
} // namespace boost::openmethod

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_MINIMAL_RTTI_HPP
#define BOOST_OPENMETHOD_POLICY_MINIMAL_RTTI_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
namespace boost::openmethod::policies {

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_STD_RTTI_HPP
#define BOOST_OPENMETHOD_POLICY_STD_RTTI_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#ifndef BOOST_NO_RTTI
#include <typeindex>

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICIES_STANDARD_ERROR_OUTPUT_HPP
#define BOOST_OPENMETHOD_POLICIES_STANDARD_ERROR_OUTPUT_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <boost/openmethod/detail/ostdstream.hpp>
namespace boost::openmethod::policies {

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_THROW_ERROR_HPP
#define BOOST_OPENMETHOD_POLICY_THROW_ERROR_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <sstream>
#include <stdexcept>

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_VECTORED_ERROR_HPP
#define BOOST_OPENMETHOD_POLICY_VECTORED_ERROR_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <functional>
#include <variant>
@@ -15,7 +15,7 @@ namespace boost::openmethod::policies {
struct default_error_handler : error_handler {
using error_variant = std::variant<
openmethod_error, not_implemented_error, unknown_class_error,
openmethod_error, no_overrider, missing_class,
fast_perfect_hash_error, final_error, static_slot_error,
static_stride_error>;
@@ -43,7 +43,7 @@ struct default_error_handler : error_handler {
if constexpr (Registry::has_output) {
auto& os = Registry::template policy<policies::output>::os;
if (auto error = std::get_if<not_implemented_error>(&error_v)) {
if (auto error = std::get_if<no_overrider>(&error_v)) {
os << "no applicable overrider for ";
Registry::template policy<policies::rtti>::type_name(
error->method, os);
@@ -60,7 +60,7 @@ struct default_error_handler : error_handler {
os << ")\n";
} else if (
auto error = std::get_if<unknown_class_error>(&error_v)) {
auto error = std::get_if<missing_class>(&error_v)) {
os << "unknown class ";
Registry::template policy<policies::rtti>::type_name(
error->type, os);

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_VPTR_MAP_HPP
#define BOOST_OPENMETHOD_POLICY_VPTR_MAP_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <unordered_map>
@@ -42,7 +42,7 @@ class vptr_map : public vptr {
//! @ref IdsToVptr objects.
//! @param first The beginning of the range.
//! @param last The end of the range.
template<typename ForwardIterator>
template<class ForwardIterator>
static void initialize(ForwardIterator first, ForwardIterator last) {
for (auto iter = first; iter != last; ++iter) {
for (auto type_iter = iter->type_id_begin();
@@ -65,7 +65,7 @@ class vptr_map : public vptr {
//! If the registry contains the @ref runtime_checks policy, checks that
//! the map contains the type id. If it does not, and if the registry
//! contains a @ref error_handler policy, calls its
//! @ref error function with a @ref unknown_class_error value, then
//! @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @tparam Class A registered class.
@@ -79,7 +79,7 @@ class vptr_map : public vptr {
if constexpr (Registry::has_runtime_checks) {
if (iter == vptrs.end()) {
if constexpr (Registry::has_error_handler) {
unknown_class_error error;
missing_class error;
error.type = type;
Registry::error_handler::error(error);
}

View File

@@ -6,7 +6,7 @@
#ifndef BOOST_OPENMETHOD_POLICY_VPTR_VECTOR_HPP
#define BOOST_OPENMETHOD_POLICY_VPTR_VECTOR_HPP
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <variant>
#include <vector>
@@ -62,12 +62,14 @@ struct vptr_vector : vptr {
//! IdsToVptr objects.
//! @param first The beginning of the range.
//! @param last The end of the range.
template<typename ForwardIterator>
template<class ForwardIterator, class... Options>
static auto
initialize(ForwardIterator first, ForwardIterator last) -> void {
std::size_t size;
if constexpr (has_type_hash) {
auto [_, max_value] = type_hash::initialize(first, last);
auto [_, max_value] =
type_hash::template initialize<ForwardIterator, Options...>(
first, last);
size = max_value + 1;
} else {
size = 0;
@@ -121,7 +123,7 @@ struct vptr_vector : vptr {
//! If the registry contains the @ref runtime_checks policy, verifies
//! that the index falls within the limits of the vector. If it does
//! not, and if the registry contains a @ref error_handler policy, calls
//! its @ref error function with a @ref unknown_class_error value, then
//! its @ref error function with a @ref missing_class value, then
//! terminates the program with @ref abort.
//!
//! @tparam Class A registered class.
@@ -148,7 +150,7 @@ struct vptr_vector : vptr {
if (index >= max_index) {
if constexpr (Registry::has_error_handler) {
unknown_class_error error;
missing_class error;
error.type = dynamic_type;
Registry::error_handler::error(error);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,786 +0,0 @@
#ifndef BOOST_OPENMETHOD_REGISTRY_HPP
#define BOOST_OPENMETHOD_REGISTRY_HPP
#include <boost/openmethod/detail/types.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/bind.hpp>
#include <stdlib.h>
#include <vector>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702)
#endif
namespace boost::openmethod {
//! Namespace containing the policy framework.
//!
//! A registry contains a set of policies that control how certain operations
//! are performed. For example, the "rtti" policy provides type information,
//! implements dynamic casting, etc. It can be replaced to interface with custom
//! RTII systems (like LLVM's).
//!
//! A policy must derive from one of the policy base classes, also known as
//! _policy categories_.
//!
//! - @ref rtti: type information acquisition and manipulation.
//!
//! - @ref error_handler: perform operations when errors are detected.
//!
//! - @ref vptr: v-table pointer acquisition.
//!
//! - @ref type_hash: hashing of `type_id`s.
//!
//! - @ref output: output stream for logging and debugging.
//!
//! - @ref runtime_checks: detect and report common errors.
//!
//! - @ref trace: report how dispatch tables are built.
//!
//! - @ref n2216: handle ambiguities according to the N2216 proposal.
//!
//! Policies are implemented as Boost.MP11 quoted meta-functions. A policy class
//! must contain a `template<class Registry> struct fn` that provides a set of
//! _static_ members, fulfilling the requirements specified in the policy's
//! category. Registries instantiate policies by passing themselves to the
//! nested `fn` class templates.
//!
//! There are two reason for this design.
//!
//! Some policies are "stateful": they contain static _data_ members. Since
//! several registries can co-exist in the same program, each stateful policy
//! needs its own, separate set of static data members. For example, @ref
//! vptr_vector, a "vptr" policy, contains a static vector of vptrs, which
//! cannot be shared with other registries.
//!
//! Some policies need access to other policies in the same registry. They can
//! be accessed via the `Registry` template parameter. For example, @ref
//! vptr_vector hashes type_ids before using them as an indexes, if `Registry`
//! cotains a `type_hash` policy. It performs out-of-bounds checks if `Registry`
//! contains the `runtime_checks` policy. If an error is detected, it invokes
//! the @ref error_handler policy if there is one.
//!
//! The last three policies (runtime_checks, trace and n2216) act like flags,
//! and enabling some sections of code. They can be used as-is, without the need
//! for subclassing.
#ifdef __MRDOCS__
//! Blueprint for a lightweight output stream (exposition only).
struct LightweightOutputStream {
LightweightOutputStream& operator<<(const char* str);
LightweightOutputStream& operator<<(const std::string_view& view);
LightweightOutputStream& operator<<(const void* value);
LightweightOutputStream& operator<<(std::size_t value);
};
#endif
namespace policies {
#ifdef __MRDOCS__
//! Requirements for policy categories (exposition only)
//!
struct PolicyCategory {
using category = PolicyCategory;
};
//! Requirements for policies (exposition only)
//!
struct Policy : PolicyCategory {
template<class Registry>
struct fn;
};
//! Requirements for IdsToVptr (exposition only)
//!
struct IdsToVptr {
//! Returns an iterator to the beginning of a range of `type_id`s for a
//! single registered class.
auto type_id_begin() const;
//! Returns an iterator to the end of a range of `type_id`s for a
//! single registered class.
auto type_id_end() const;
//! Returns a range of `type_id`s that this assignment applies to.
auto vptr() const -> const vptr_type&;
};
#endif
//! Policy for runtime type information.
//!
//! A @e rtti policy is responsible for acquiring and manipulating type
//! information, dynamic casting, and detecting polymorphic classes.
//!
//! @par Requirements
//!
//! A subclass of `rtti` must contain a `fn<Registry>` class template
//! that fulfills the requirements of @ref rtti::fn.
struct rtti {
using category = rtti;
//! Default implementations of some `rtti` requirements.
struct defaults {
//! Default implementation for `type_index`.
//!
//! @param type A `type_id`.
//!
//! @return `type` itself.
static auto type_index(type_id type) -> type_id {
return type;
}
//! Default implementation of `type_name`.
//!
//! Executes `stream << "type_id(" << type << ")"`.
//!
//! @param type A `type_id`.
//! @param stream A stream to write to.
template<typename Stream>
static void type_name(type_id type, Stream& stream) {
stream << "type_id(" << type << ")";
}
};
#ifdef __MRDOCS__
//! Requirements for `rtti` policies (exposition only).
//!
//! This class is for _exposition only_. It is the responsibility of
//! subclasses to provide a `fn` class template that contains the members
//! listed on this page.
template<class Registry>
struct fn {
//! Tests if a class is polymorphic.
//!
//! @tparam Class A class.
template<class Class>
static constexpr bool is_polymorphic = std::is_polymorphic_v<Class>;
//! Returns the static @ref type_id of a type.
//!
//! @note `Class` is not necessarily a @e registered class. This
//! function is also called to acquire the type_id of non-virtual
//! parameters, library types, etc, for diagnostic and trace purposes.
//!
//! @tparam Class A class.
//! @return The static type_id of Class.
template<class Class>
static auto static_type() -> type_id;
//! Returns the dynamic @ref type_id of an object.
//!
//! @tparam Class A registered class.
//! @param obj A reference to an instance of `Class`.
//! @return The type_id of `obj`'s class.
template<class Class>
static auto dynamic_type(const Class& obj) -> type_id;
//! Writes a representation of a @ref type_id to a stream.
//!
//! @tparam Stream A LightweightOutputStream.
//! @param type The `type_id` to write.
//! @param stream The stream to write to.
template<typename Stream>
static auto type_name(type_id type, Stream& stream) -> void;
//! Returns a key that uniquely identifies a class.
//!
//! @param type A `type_id`.
//! @return A unique value that identifies a class with the given
//! `type_id`.
static auto type_index(type_id type);
//! Casts an object to a type.
//!
//! @tparam D A reference to a subclass of `B`.
//! @tparam B A registered class.
//! @param obj A reference to an instance of `B`.
template<typename D, typename B>
static auto dynamic_cast_ref(B&& obj) -> D;
};
#endif
};
#ifdef __MRDOCS__
struct std_rtti;
struct static_rtti;
#endif
//! Policy for deferred type id collection.
//!
//! Some custom RTTI systems rely on static constructors to assign type ids.
//! OpenMethod itself relies on static constructors to register classes, methods
//! and overriders. This creates order-of-initialization issues. Deriving a @e
//! rtti policy from this class - instead of just `rtti` - causes the collection
//! of type ids to be deferred until the first call to @ref update.
struct deferred_static_rtti : rtti {};
//! Policy for error handling.
//!
//! A @e error_handler policy runs code before the library terminats the program
//! due to an error. This can be useful for throwing, logging, cleanup, or other
//! actions.
//!
//! @par Requirements
//!
//! A subclass of `error_handler` must contain a `fn<Registry>` class template
//! that fulfills the requirements of @ref error_handler::fn.
struct error_handler {
using category = error_handler;
#ifdef __MRDOCS__
//! Requirements for `error_handler` policies (exposition only).
//!
//! This class is for _exposition only_. It is the responsibility of
//! subclasses to provide a `fn` class template that contains the members
//! listed on this page.
template<class Registry>
struct fn {
//! Called when an error is detected.
//!
//! `error` is a function, or a set of functions, that can be called
//! with an instance of any subclass of `openmethod_error`.
static auto error(const auto& error) -> void;
};
#endif
};
#ifdef __MRDOCS__
struct default_error_handler;
struct throw_error_handler;
#endif
//! Policy for v-table pointer acquisition.
//!
//! @par Requirements
//!
//! A subclass of `vptr` must contain a `fn<Registry>` class template
//! that fulfills the requirements of @ref vptr::fn.
struct vptr {
using category = vptr;
#ifdef __MRDOCS__
//! Requirements for `vptr` policies (exposition only)
//!
//! This class is for _exposition only_. It is the responsibility of
//! subclasses to provide a `fn` class template that contains the members
//! listed on this page.
template<class Registry>
struct fn {
//! Stores the v-table pointers.
//! @tparam ForwardIterator An iterator to a range of const
//! @ref `IdsToVptr` objects.
//! @param first The beginning of the range.
//! @param last The end of the range.
template<typename ForwardIterator>
static auto initialize(ForwardIterator first, ForwardIterator last);
//! Returns a *reference* to a v-table pointer for an object.
//!
//! @tparam Class A registered class.
//! @param arg A reference to a const object of type `Class`.
//! @return A reference to a the v-table pointer for `Class`.
template<class Class>
static auto dynamic_vptr(const Class& arg) -> const vptr_type&;
//! Releases the resources allocated by `initialize`. This function is
//! optional.
static auto finalize() -> void;
};
#endif
};
//! Policy to add an indirection to pointers to v-tables.
//!
//! If this policy is present, constructs like @ref virtual_ptr, @ref
//! inplace_vptr, @ref vptr_vector, etc store pointers to pointers to v-tables.
//! These indirect pointers remain valid after a call to @ref initialize, even
//! though the v-tables move to different locations. This is useful in presence
//! of dynamic loading.
//!
//! @par Requirements
//!
//! None. `indirect_vptr` can be added to a registry's policy list as-is.
struct indirect_vptr final {
using category = indirect_vptr;
template<class Registry>
struct fn {};
};
#ifdef __MRDOCS__
class vptr_vector;
template<class MapFn>
class vptr_map;
#endif
//! Policy for type_id hashing.
//!
//! A @e type_hash policy calculates an integer hash for a @ref type_id.
//!
//! @par Requirements
//!
//! A subclass of `type_hash` must contain a `fn<Registry>` class template
//! that fulfills the requirements of @ref type_hash::fn.
struct type_hash {
using category = type_hash;
#ifdef __MRDOCS__
//! Requirements for `type_hash` policies (exposition only)
//! @tparam Registry The registry containing this policy.
//!
//! This class is for _exposition only_. It is the responsibility of
//! subclasses to provide a `fn` class template that contains the members
//! listed on this page.
template<class Registry>
struct fn {
//! Initializes the hash table.
//! @tparam ForwardIterator An iterator to a range of const
//! @ref IdsToVptr objects.
//! @param first The beginning of the range.
//! @param last The end of the range.
//! @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>;
//! Hashes a `type_id`.
//! @param type A @ref type_id.
//! @return A hash value for the given `type_id`.
static auto hash(type_id type) -> std::size_t;
//! Releases the resources allocated by `initialize`. This function is
//! optional.
static auto finalize() -> void;
};
#endif
};
#ifdef __MRDOCS__
class fast_perfect_hash;
#endif
//! Policy for writing diagnostics and trace.
//!
//! If an `output` policy is present, the default error handler uses it to write
//! error messages to its output stream. `initialize` can also use it to write
//! trace messages.
//!
//! @par Requirements
//!
//! A subclass of `output` must contain a `fn<Registry>` class template
//! that fulfills the requirements of @ref output::fn.
struct output {
using category = output;
#ifdef __MRDOCS__
//! Requirements for `output` policies (exposition only)
//!
//! This class is for _exposition only_. It is the responsibility of
//! subclasses to provide a `fn` class template that contains the members
//! listed on this page.
template<class Registry>
struct fn {
//! A @ref LightweightOutputStream.
inline static LightweightOutputStream os;
};
#endif
};
#ifdef __MRDOCS__
struct stderr_output;
#endif
//! Policy for tracing.
//!
//! If `trace` is present, trace instructions are added to various parts of the
//! initialization process (dispatch table construction, hash factors search,
//! etc). These instructions are executed only if `trace::fn<Registry>::on` is
//! set to `true`. The default value of `on` is `true` if environment variable
//! `BOOST_OPENMETHOD_TRACE` is set to the string "1". At the moment, any other
//! value disables tracing.
//!
//! `trace` requires an `output` policy to be present. Trace is written to its
//! output stream.
//!
//! The exact format of the trace output is not specified, and may change at any
//! time. The only guarantee is that it is detailed and comprehensive, and makes
//! it possible to troubleshoot problems like missing class registrations,
//! missing or ambiguous overriders, etc.
struct trace final {
using category = trace;
template<class Registry>
struct fn {
inline static bool on = []() {
#ifdef _MSC_VER
char* env;
std::size_t len;
auto result =
_dupenv_s(&env, &len, "BOOST_OPENMETHOD_TRACE") == 0 && env &&
len == 2 && *env == '1';
free(env);
return result;
#else
auto env = getenv("BOOST_OPENMETHOD_TRACE");
return env && *env++ == '1' && *env++ == 0;
#endif
}();
};
};
//! Policy for runtime sanity checks.
//!
//! If this policy is present, various checks are performed at runtime.
//! Currently they all attempt to detect missing class registrations.
struct runtime_checks final {
using category = runtime_checks;
template<class Registry>
struct fn {};
};
//! Policy for N2216 ambiguity resolution.
//!
//! If this policy is present, additional steps are taken to select a single
//! overrider in presence of ambiguous overriders sets, according to the rules
//! defined in the N2216 paper. If the normal resolution procedure fails to
//! select a single overrider, the following steps are applied, in order:
//!
//! - If the return types of the remaining overriders are all polymorphic and
//! covariant, and one of the return types is more specialized thjat all the
//! others, use it.
//!
//! - Otherwise, pick one of the overriders. Which one is used is unspecified,
//! but remains the same throughtout the program, and across different runs of
//! the same program.
struct n2216 final {
using category = n2216;
template<class Registry>
struct fn {};
};
} // namespace policies
namespace detail {
struct registry_base {};
template<typename T>
constexpr bool is_registry = std::is_base_of_v<registry_base, T>;
template<typename T>
constexpr bool is_not_void = !std::is_same_v<T, void>;
template<
class Registry, class Index,
class Size = mp11::mp_size<typename Registry::policy_list>>
struct get_policy_aux {
using type = typename mp11::mp_at<
typename Registry::policy_list, Index>::template fn<Registry>;
};
template<class Registry, class Size>
struct get_policy_aux<Registry, Size, Size> {
using type = void;
};
using class_catalog = detail::static_list<detail::class_info>;
using method_catalog = detail::static_list<detail::method_info>;
template<class Policies, class...>
struct with_aux;
template<class Policies>
struct with_aux<Policies> {
using type = Policies;
};
template<class Policies, class Policy, class... MorePolicies>
struct with_aux<Policies, Policy, MorePolicies...> {
using replace = mp11::mp_replace_if_q<
Policies,
mp11::mp_bind_front_q<
mp11::mp_quote_trait<std::is_base_of>, typename Policy::category>,
Policy>;
using replace_or_add = std::conditional_t<
std::is_same_v<replace, Policies>, mp11::mp_push_back<Policies, Policy>,
replace>;
using type = typename with_aux<replace_or_add, MorePolicies...>::type;
};
template<class Policies, class...>
struct without_aux;
template<class Policies>
struct without_aux<Policies> {
using type = Policies;
};
template<class Policies, class Policy, class... MorePolicies>
struct without_aux<Policies, Policy, MorePolicies...> {
using type = typename without_aux<
mp11::mp_remove_if_q<
Policies,
mp11::mp_bind_front_q<
mp11::mp_quote_trait<std::is_base_of>,
typename Policy::category>>,
MorePolicies...>::type;
};
template<class...>
struct use_class_aux;
} // namespace detail
//! A collection of methods and their associated dispatch data.
//!
//! Methods exist in a registry, which also contains descriptions for all the
//! classes that can appear in the methods, their overriders, and method calls.
//!
//! Before calling a method, the @ref initialize function must be called for its
//! registry to set up the dispatch tables. This is typically done at the
//! beginning of `main`.
//!
//! Multiple registries can co-exist in the same program. They must be
//! initialized independently. Classes referenced by methods in different
//! registries must be registered with each registry individually.
//!
//!
//!
//! @tparam Policies The policies used in the registry.
//!
//! @par Requirements
//!
//! `Policies` must be models of @ref policies::Policy. There may be at most one
//! policy per category, i.e. `Policies::category...` must all be different.
//!
//! @see @ref policies
template<class... Policies>
class registry : detail::registry_base {
inline static detail::class_catalog classes;
inline static detail::method_catalog methods;
template<class...>
friend struct detail::use_class_aux;
template<typename Name, typename ReturnType, class Registry>
friend class method;
struct compiler;
inline static std::vector<detail::word> dispatch_data;
inline static bool initialized;
public:
//! Initializes the registry.
//!
//! `initialize` must be called, typically at the beginning of `main`,
//! before using any of the methods in the registry. It sets up the
//! v-tables, multi-method dispatch tables, and any other data required by
//! the policies.
//!
//! @note
//! A translation unit that contains a call to `initialize` must include the
//! `<boost/openmethod/initialize.hpp>` header.
//!
//! @par Errors
//!
//! @li @ref unknown_class_error: A class used in a virtual parameter was
//! not registered.
//!
//! In addition, policies may encounter and report errors.
static auto initialize();
//! Checks if the registry is initialized.
//!
//! Checks if `initialize` has been called for this registry, and report an
//! error if not.
//!
//! @par Errors
//!
//! @li @ref not_initialized_error: The registry is not initialized.
static void check_initialized();
//! Releases the resources held by the registry.
//!
//! `finalize` may be called to release any resources allocated by
//! `initialize`.
//!
//! @note
//! A translation unit that contains a call to `finalize` must include the
//! `<boost/openmethod/initialize.hpp>` header.
static void finalize();
//! A pointer to the virtual table for a registered class.
//!
//! `static_vptr` is set by @ref initialize to the address of the class's
//! virtual table. It remains valid until the next call to `initialize` or
//! `finalize`.
//!
//! @tparam Class A registered class.
template<class Class>
inline static vptr_type static_vptr;
//! The list of policies selected in a registry.
//!
//! `policy_list` is a Boost.Mp11 list containing the policies passed to the
//! @ref registry clas template.
//!
//! @tparam Class A registered class.
using policy_list = mp11::mp_list<Policies...>;
//! Returns the policy for a policy category.
//!
//! `policy` searches for a policy that derives from the specified @ref
//! PolicyCategory. If none is found, it aliases to `void`. Otherwise, it
//! aliases to the policy's `fn` class template, instantiated for this
//! registry.
//!
//! @tparam Category A model of @ref policies::PolicyCategory.
template<class Category>
using policy = typename detail::get_policy_aux<
registry,
mp11::mp_find_if_q<
policy_list,
mp11::mp_bind_front_q<
mp11::mp_quote_trait<std::is_base_of>, Category>>>::type;
//! Returns a copy of this registry, with additional policies.
//!
//! `with` aliases to a registry containing `NewPolicies`, in addition to
//! this registry's policies that are not in the same category as any of the
//! `NewPolicies`.
//!
//! @tparam NewPolicies Models of @ref policies::Policy.
template<class... NewPolicies>
using with = boost::mp11::mp_apply<
registry, typename detail::with_aux<policy_list, NewPolicies...>::type>;
//! Returns a copy of this registry, with some policies removed.
//!
//! `without` returns a copy of this registry, without the policies that
//! derive from `Categories`.
//!
//! @tparam Categories Models of @ref policies::PolicyCategory.
template<class... Categories>
using without = boost::mp11::mp_apply<
registry,
typename detail::without_aux<policy_list, Categories...>::type>;
//! The registry's rtti policy.
using rtti = policy<policies::rtti>;
//! The registry's vptr policy if it contains one, or `void`.
using vptr = policy<policies::vptr>;
//! `true` if the registry has a vptr policy.
static constexpr auto has_vptr = !std::is_same_v<vptr, void>;
//! The registry's error_handler policy if it contains one, or `void`.
using error_handler = policy<policies::error_handler>;
//! `true` if the registry has an error_handler policy.
static constexpr auto has_error_handler =
!std::is_same_v<error_handler, void>;
//! The registry's output policy if it contains one, or `void`.
using output = policy<policies::output>;
//! `true` if the registry has an output policy.
static constexpr auto has_output = !std::is_same_v<output, void>;
//! The registry's trace policy if it contains one, or `void`.
using trace = policy<policies::trace>;
//! `true` if the registry has a trace policy.
static constexpr auto has_trace = !std::is_same_v<trace, void>;
//! `true` if the registry has a deferred_static_rtti policy.
static constexpr auto has_deferred_static_rtti =
!std::is_same_v<policy<policies::deferred_static_rtti>, void>;
//! `true` if the registry has a runtime_checks policy.
static constexpr auto has_runtime_checks =
!std::is_same_v<policy<policies::runtime_checks>, void>;
//! `true` if the registry has an indirect_vptr policy.
static constexpr auto has_indirect_vptr =
!std::is_same_v<policy<policies::indirect_vptr>, void>;
//! `true` if the registry has a n2216 policy.
static constexpr auto has_n2216 =
!std::is_same_v<policy<policies::n2216>, void>;
};
template<class... Policies>
inline void registry<Policies...>::check_initialized() {
if constexpr (registry::has_runtime_checks) {
if (!initialized) {
if constexpr (registry::has_error_handler) {
error_handler::error(not_initialized_error());
}
abort();
}
}
}
template<class Registry, class Stream>
auto call_error::write_aux(Stream& os, const char* subtype) const -> void {
using namespace detail;
using namespace policies;
os << "invalid call to ";
Registry::template policy<rtti>::type_name(method, os);
os << "(";
auto comma = "";
for (auto ti : range{types, types + arity}) {
os << comma;
Registry::template policy<rtti>::type_name(ti, os);
comma = ", ";
}
os << "): " << subtype;
}
template<class Registry, class Stream>
auto unknown_class_error::write(Stream& os) const -> void {
os << "unknown class ";
Registry::rtti::type_name(type, os);
}
template<class Registry, class Stream>
auto final_error::write(Stream& os) const -> void {
os << "invalid call to final construct: static type = ";
Registry::rtti::type_name(static_type, os);
os << ", dynamic type = ";
Registry::rtti::type_name(dynamic_type, os);
}
template<class Registry, class Stream>
auto fast_perfect_hash_error::write(Stream& os) const -> void {
os << "could not find hash factors after " << attempts << "s using "
<< buckets << " buckets\n";
}
template<class Registry, class Stream>
auto static_offset_error::write(Stream& os) const -> void {
os << "static offset error in ";
Registry::rtti::type_name(method, os);
os << ": expected " << expected << ", got " << actual;
}
} // namespace boost::openmethod
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // BOOST_OPENMETHOD_REGISTRY_HPP

View File

@@ -5,13 +5,17 @@
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
# Official repository: https://github.com/boostorg/url
# Official repository: https://github.com/boostorg/openmethod
#
message(STATUS "Building tests")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_definitions(BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS)
endif()
# Custom target used by the boost super-project
if(NOT TARGET tests)
if (NOT TARGET tests)
add_custom_target(tests)
set_property(TARGET tests PROPERTY FOLDER Dependencies)
endif()
@@ -59,3 +63,35 @@ foreach(test_cpp ${test_cpp_files})
add_test(NAME ${test} COMMAND ${test})
add_dependencies(tests ${test})
endforeach()
add_executable(test_mix_release_debug mix_release_debug/main.cpp mix_release_debug/lib.cpp)
target_link_libraries(test_mix_release_debug Boost::openmethod Boost::unit_test_framework)
add_test(NAME test_mix_release_debug COMMAND test_mix_release_debug)
add_dependencies(tests test_mix_release_debug)
function(openmethod_compile_fail_test testname fail_regex)
add_library("compile-fail-${testname}" STATIC EXCLUDE_FROM_ALL "${testname}.cpp")
target_link_libraries("compile-fail-${testname}" PRIVATE Boost::openmethod)
add_test(
NAME "openmethod-${testname}"
COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target "compile-fail-${testname}" --config $<CONFIG>)
set_property(TEST "openmethod-${testname}" PROPERTY PASS_REGULAR_EXPRESSION "${fail_regex}")
endfunction()
openmethod_compile_fail_test(
compile_fail_non_polymorphic_virtual_parameter "parameter is not a polymorphic class")
openmethod_compile_fail_test(
compile_fail_non_polymorphic_virtual_ptr ".*")
openmethod_compile_fail_test(
compile_fail_virtual_parameter_to_value "virtual_traits not specialized for type")
openmethod_compile_fail_test(
compile_fail_virtual_ptr_different_registries "registry mismatch")
openmethod_compile_fail_test(
compile_fail_virtual_ptr_other
"virtual_ptr<> is required in overrider in same position as in method")
openmethod_compile_fail_test(
compile_fail_virtual_ptr_ref_to_value "different virtual_ptr<> reference categories")
openmethod_compile_fail_test(
compile_fail_virtual_ptr_shared_not_const "std::shared_ptr cannot be passed by non-const lvalue reference")
openmethod_compile_fail_test(
compile_fail_virtual_ptr_value_to_ref "different virtual_ptr<> reference categories")

View File

@@ -35,6 +35,9 @@ for local src in [ glob test_*.cpp ]
run $(src) unit_test_framework ;
}
run mix_release_debug/main.cpp mix_release_debug/lib.cpp unit_test_framework ;
for local src in [ glob compile_fail_*.cpp ]
{
compile-fail $(src) ;

View File

@@ -12,8 +12,10 @@ struct Cat {
}
};
BOOST_OPENMETHOD(
poke, (virtual_ptr<Cat, release_registry>), void, debug_registry);
struct other_registry : default_registry::without<policies::type_hash>::with<
policies::runtime_checks> {};
BOOST_OPENMETHOD(poke, (virtual_ptr<Cat>), void, other_registry);
int main() {
Cat felix;

View File

@@ -4,7 +4,7 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
using namespace boost::openmethod;

View File

@@ -0,0 +1,22 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <iostream>
#ifdef BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
#undef BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
#else
#define BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
#endif
#include "lib.hpp"
using namespace boost::openmethod;
BOOST_OPENMETHOD_CLASSES(Animal, Cat);
BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Cat>, std::ostream& os), void) {
os << "hiss";
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_OPENMETHOD_TEST_MIX_RELEASE_DEBUG_LIB_HPP
#define BOOST_OPENMETHOD_TEST_MIX_RELEASE_DEBUG_LIB_HPP
#include <boost/openmethod.hpp>
struct Animal {
virtual ~Animal() {
}
};
struct Cat : Animal {};
BOOST_OPENMETHOD(
poke, (boost::openmethod::virtual_ptr<Animal>, std::ostream&), void);
#endif // BOOST_OPENMETHOD_TEST_MIX_RELEASE_DEBUG_LIB_HPP

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>
#include <boost/openmethod.hpp>
#include <boost/openmethod/initialize.hpp>
#include "lib.hpp"
using namespace boost::openmethod;
BOOST_AUTO_TEST_CASE(mix_release_debug) {
default_registry::error_handler::set(
[](const default_registry::error_handler::error_variant& error) {
std::visit([](auto&& arg) { throw arg; }, error);
});
BOOST_CHECK_THROW(initialize(), odr_violation);
}

View File

@@ -0,0 +1,85 @@
// Copyright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>
#include <boost/openmethod.hpp>
#include <boost/openmethod/policies/throw_error_handler.hpp>
#include <boost/openmethod/initialize.hpp>
#include "test_util.hpp"
using namespace boost::openmethod;
struct Animal {
virtual ~Animal() {
}
};
struct Dog : Animal {};
struct Bulldog : Dog {};
namespace TEST_NS {
struct registry : test_registry_<__COUNTER__>::with<
policies::runtime_checks, policies::throw_error_handler> {
};
BOOST_OPENMETHOD(poke, (boost::openmethod::virtual_<Animal&>), void, registry);
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&), void) {
}
BOOST_OPENMETHOD_CLASSES(Animal, registry);
BOOST_AUTO_TEST_CASE(unknown_class_overrider) {
BOOST_CHECK_THROW(initialize<registry>(), missing_class);
}
} // namespace TEST_NS
namespace TEST_NS {
struct registry : test_registry_<__COUNTER__>::with<
policies::runtime_checks, policies::throw_error_handler> {
};
BOOST_OPENMETHOD(poke, (boost::openmethod::virtual_<Animal&>), void, registry);
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&), void) {
}
BOOST_OPENMETHOD_CLASSES(Animal, registry);
BOOST_OPENMETHOD_CLASSES(Dog, registry); // missing base class
BOOST_AUTO_TEST_CASE(missing_base_class) {
BOOST_CHECK_THROW(initialize<registry>(), missing_base);
}
} // namespace TEST_NS
namespace TEST_NS {
struct registry : test_registry_<__COUNTER__>::with<
policies::runtime_checks, policies::throw_error_handler> {
};
BOOST_OPENMETHOD(poke, (boost::openmethod::virtual_<Animal&>), void, registry);
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&), void) {
}
BOOST_OPENMETHOD_CLASSES(Animal, Dog, registry);
BOOST_AUTO_TEST_CASE(unknown_class_call) {
initialize<registry>();
Bulldog hector;
BOOST_CHECK_THROW(poke(hector), missing_class);
}
} // namespace TEST_NS

View File

@@ -104,14 +104,14 @@ BOOST_AUTO_TEST_CASE(test_use_classes_linear) {
struct D4 : D3 {};
struct D5 : D4 {};
using policy = test_registry_<__COUNTER__>;
struct registry : test_registry_<__COUNTER__> {};
BOOST_OPENMETHOD_CLASSES(Base, D1, D2, D3, policy);
BOOST_OPENMETHOD_CLASSES(D2, D3, policy);
BOOST_OPENMETHOD_CLASSES(D3, D4, policy);
BOOST_OPENMETHOD_CLASSES(D4, D5, D3, policy);
BOOST_OPENMETHOD_CLASSES(Base, D1, D2, D3, registry);
BOOST_OPENMETHOD_CLASSES(D2, D3, registry);
BOOST_OPENMETHOD_CLASSES(D3, D4, registry);
BOOST_OPENMETHOD_CLASSES(D4, D5, D3, registry);
auto comp = policy::initialize();
auto comp = initialize<registry>();
auto base = get_class<Base>(comp);
auto d1 = get_class<D1>(comp);
@@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(test_use_classes_diamond) {
std::vector<class_*> actual, expected;
auto comp = test_registry::initialize();
auto comp = initialize<test_registry>();
auto a = get_class<A>(comp);
auto b = get_class<B>(comp);
@@ -239,7 +239,7 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a_b1_c) {
BOOST_OPENMETHOD_REGISTER(use_classes<A, C, test_registry>);
ADD_METHOD(B);
auto comp = test_registry::initialize();
auto comp = initialize<test_registry>();
BOOST_TEST_REQUIRE(check(comp[m_B])->slots.size() == 1u);
BOOST_TEST(check(comp[m_B])->slots[0] == 0u);
@@ -269,7 +269,7 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_c1) {
ADD_METHOD(A);
ADD_METHOD(B);
ADD_METHOD(C);
auto comp = test_registry::initialize();
auto comp = initialize<test_registry>();
BOOST_TEST_REQUIRE(check(comp[m_A])->slots.size() == 1u);
BOOST_TEST(check(comp[m_A])->slots[0] == 0u);
@@ -310,7 +310,7 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1) {
ADD_METHOD(B);
ADD_METHOD(C);
ADD_METHOD(D);
auto comp = test_registry::initialize();
auto comp = initialize<test_registry>();
BOOST_TEST_REQUIRE(check(comp[m_A])->slots.size() == 1u);
BOOST_TEST(check(comp[m_A])->slots[0] == 0u);
@@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1_e2) {
ADD_METHOD_N(E, 1);
ADD_METHOD_N(E, 2);
ADD_METHOD_N(E, 3);
auto comp = test_registry::initialize();
auto comp = initialize<test_registry>();
BOOST_TEST_REQUIRE(check(comp[m_A])->slots.size() == 1u);
BOOST_TEST(check(comp[m_A])->slots[0] == 0u);
@@ -415,7 +415,7 @@ BOOST_AUTO_TEST_CASE(test_assign_slots_a1_c1_b1) {
ADD_METHOD(A);
ADD_METHOD(B);
ADD_METHOD(C);
auto comp = test_registry::initialize();
auto comp = initialize<test_registry>();
BOOST_TEST_REQUIRE(check(comp[m_A])->slots.size() == 1u);
BOOST_TEST(check(comp[m_A])->slots[0] == 0u);

View File

@@ -9,7 +9,7 @@
#define BOOST_TEST_MODULE core
#include <boost/test/unit_test.hpp>
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
using namespace boost::openmethod;
using namespace boost::openmethod::detail;
@@ -17,7 +17,7 @@ namespace mp11 = boost::mp11;
#include <boost/openmethod.hpp>
#include <boost/openmethod/inplace_vptr.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include "test_util.hpp"
@@ -269,41 +269,6 @@ static_assert(
} // namespace test_use_classes
// -----------------------------------------------------------------------------
// static_slots
namespace test_static_slots {
struct Animal;
}
namespace boost::openmethod::detail {
template<>
struct static_offsets<method<
void,
void(
virtual_<test_static_slots::Animal&>,
virtual_<test_static_slots::Animal&>)>> {
static constexpr std::size_t slots[] = {0, 1};
};
} // namespace boost::openmethod::detail
namespace test_static_slots {
struct Animal {
virtual ~Animal() {
}
};
using poke = method<void, void(virtual_<Animal&>)>;
static_assert(!has_static_offsets<poke>::value);
using meet = method<void, void(virtual_<Animal&>, virtual_<Animal&>)>;
static_assert(has_static_offsets<meet>::value);
} // namespace test_static_slots
namespace TEST_NS {
struct Animal {
@@ -330,7 +295,7 @@ static_assert(detail::has_vptr_fn<Animal, test_registry>);
static_assert(!detail::has_vptr_fn<Animal, default_registry>);
BOOST_AUTO_TEST_CASE(vptr_from_function) {
test_registry::initialize();
initialize<test_registry>();
BOOST_TEST(detail::acquire_vptr<test_registry>(Animal{}) == &value);
}

View File

@@ -93,7 +93,7 @@ BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & cat, std::ostream& os), void) {
}
BOOST_AUTO_TEST_CASE(custom_rtti_simple_projection) {
test_registry::initialize();
initialize<test_registry>();
Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester");
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(custom_rtti_simple) {
BOOST_TEST(Animal::static_type == 0u);
BOOST_TEST(Dog::static_type == 1u);
BOOST_TEST(Cat::static_type == 2u);
test_registry::initialize();
initialize<test_registry>();
Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester");
@@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(virtual_base) {
BOOST_TEST(Animal::static_type == 0u);
BOOST_TEST(Dog::static_type == 1u);
BOOST_TEST(Cat::static_type == 2u);
test_registry::initialize();
initialize<test_registry>();
Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester");
@@ -497,7 +497,7 @@ BOOST_OPENMETHOD_OVERRIDE(meet, (Dog&, Dog&, std::ostream& os), void) {
}
BOOST_AUTO_TEST_CASE(custom_rtti_deferred) {
test_registry::initialize();
initialize<test_registry>();
Animal &&a = Dog("Snoopy"), &&b = Cat("Sylvester");

View File

@@ -10,8 +10,8 @@
#include <any>
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/policies/vptr_vector.hpp>
#include <boost/openmethod/policies/throw_error_handler.hpp>
@@ -75,7 +75,7 @@ BOOST_OPENMETHOD_OVERRIDE(name, (const Dog& dog), std::string) {
}
BOOST_AUTO_TEST_CASE(cast_args_lvalue_refs) {
test_registry::initialize();
initialize<test_registry>();
Dog spot("Spot");
BOOST_TEST(name(spot) == "Bill's dog Spot");
@@ -114,7 +114,7 @@ BOOST_OPENMETHOD_OVERRIDE(teleport, (Dog && dog), std::unique_ptr<Animal>) {
}
BOOST_AUTO_TEST_CASE(cast_args_rvalue_refs) {
test_registry::initialize();
initialize<test_registry>();
{
Dog spot("Spot");
@@ -153,7 +153,7 @@ BOOST_OPENMETHOD_OVERRIDE(name, (const Dog* dog), std::string) {
}
BOOST_AUTO_TEST_CASE(cast_args_pointer) {
test_registry::initialize();
initialize<test_registry>();
Dog spot("Spot");
BOOST_TEST(name(&spot) == "Bill's dog Spot");
@@ -186,7 +186,7 @@ BOOST_OPENMETHOD_OVERRIDE(name, (std::shared_ptr<const Dog> dog), std::string) {
}
BOOST_AUTO_TEST_CASE(cast_args_shared_ptr_by_value) {
test_registry::initialize();
initialize<test_registry>();
auto spot = std::make_shared<Dog>("Spot");
BOOST_TEST(name(spot) == "Bill's dog Spot");
@@ -227,7 +227,7 @@ BOOST_OPENMETHOD_OVERRIDE(
}
BOOST_AUTO_TEST_CASE(cast_args_shared_ptr_by_ref) {
test_registry::initialize();
initialize<test_registry>();
auto spot = std::make_shared<Dog>("Spot");
BOOST_TEST(name(spot) == "Bill's dog Spot");
@@ -260,7 +260,7 @@ BOOST_OPENMETHOD_OVERRIDE(name, (std::unique_ptr<Dog> dog), std::string) {
}
BOOST_AUTO_TEST_CASE(cast_args_unique_ptr) {
test_registry::initialize();
initialize<test_registry>();
auto spot = std::make_unique<Dog>("Spot");
BOOST_TEST(name(std::move(spot)) == "Bill's dog Spot");
@@ -318,7 +318,7 @@ BOOST_OPENMETHOD_OVERRIDE(times, (const matrix&, double), string_pair) {
}
BOOST_AUTO_TEST_CASE(simple) {
auto report = test_registry::initialize().report;
auto report = initialize<test_registry>().report;
BOOST_TEST(report.not_implemented == 0u);
BOOST_TEST(report.ambiguous == 0u);
@@ -339,7 +339,7 @@ BOOST_AUTO_TEST_CASE(simple) {
if constexpr (std::is_same_v<test_registry::vptr, policies::vptr_vector>) {
BOOST_TEST(
!detail::vptr_vector_vptrs<test_registry::registry_type>.empty());
finalize<test_registry>();
test_registry::finalize();
static_assert(detail::has_finalize_aux<
test_registry::policy<policies::vptr>>::value);
BOOST_TEST(
@@ -439,7 +439,7 @@ BOOST_OPENMETHOD_OVERRIDE(foo, (Test&), std::pair<int, int>) {
}
BOOST_AUTO_TEST_CASE(comma_in_return_type) {
test_registry::initialize();
initialize<test_registry>();
Test test;

View File

@@ -16,7 +16,7 @@ struct test_registry : bom::default_registry::without<
#include <boost/openmethod.hpp>
#include <boost/openmethod/inplace_vptr.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#define BOOST_TEST_MODULE intrusive
@@ -25,25 +25,25 @@ struct test_registry : bom::default_registry::without<
namespace bom = boost::openmethod;
using bom::virtual_;
struct Animal : bom::inplace_vptr<Animal> {
struct Animal : bom::inplace_vptr_base<Animal> {
explicit Animal(std::ostream& os);
~Animal();
std::ostream& os;
};
struct Cat : Animal, bom::inplace_vptr<Cat, Animal> {
struct Cat : Animal, bom::inplace_vptr_derived<Cat, Animal> {
explicit Cat(std::ostream& os);
~Cat();
};
struct Pet : bom::inplace_vptr<Pet> {
struct Pet : bom::inplace_vptr_base<Pet> {
explicit Pet(std::ostream& os);
~Pet();
std::string name;
std::ostream& os;
};
struct DomesticCat : Cat, Pet, bom::inplace_vptr<DomesticCat, Cat, Pet> {
struct DomesticCat : Cat, Pet, bom::inplace_vptr_derived<DomesticCat, Cat, Pet> {
explicit DomesticCat(std::ostream& os);
~DomesticCat();
};
@@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(intrusive_mode) {
struct indirect_policy : test_registry::with<bom::policies::indirect_vptr> {};
struct Indirect : bom::inplace_vptr<Indirect, indirect_policy> {};
struct Indirect : bom::inplace_vptr_base<Indirect, indirect_policy> {};
BOOST_OPENMETHOD(whatever, (virtual_<Indirect&>), void, indirect_policy);
@@ -189,7 +189,7 @@ BOOST_OPENMETHOD_OVERRIDE(whatever, (Indirect&), void) {
}
BOOST_AUTO_TEST_CASE(core_intrusive_vptr) {
indirect_policy::initialize();
bom::initialize<indirect_policy>();
Indirect i;
BOOST_TEST(
boost_openmethod_vptr(i, nullptr) ==

View File

@@ -10,8 +10,8 @@
#include <any>
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/policies/vptr_vector.hpp>
@@ -26,13 +26,13 @@ namespace TEST_NS {
using namespace test_matrices;
struct n2216 : test_registry_<__COUNTER__>::with<policies::n2216> {};
struct test_registry : test_registry_<__COUNTER__> {};
BOOST_OPENMETHOD_CLASSES(matrix, dense_matrix, n2216);
BOOST_OPENMETHOD_CLASSES(matrix, dense_matrix, test_registry);
BOOST_OPENMETHOD(
times, (virtual_<const matrix&>, virtual_<const matrix&>),
std::unique_ptr<matrix>, n2216);
std::unique_ptr<matrix>, test_registry);
BOOST_OPENMETHOD_OVERRIDE(
times, (const dense_matrix&, const matrix&),
@@ -46,13 +46,13 @@ BOOST_OPENMETHOD_OVERRIDE(
}
static_assert(std::is_same_v<
detail::virtual_type<std::unique_ptr<matrix>, n2216>, matrix>);
detail::virtual_type<std::unique_ptr<matrix>, test_registry>, matrix>);
BOOST_AUTO_TEST_CASE(covariant_return_type) {
auto compiler = n2216::initialize();
auto compiler = boost::openmethod::initialize<test_registry, n2216>();
BOOST_TEST(compiler.report.ambiguous == 0u);
// N2216: use covariant return types to resolve ambiguity.
// test_registry: use covariant return types to resolve ambiguity.
dense_matrix left, right;
auto result = times(left, right);
BOOST_TEST(result->type == DENSE_MATRIX);
@@ -64,13 +64,13 @@ namespace TEST_NS {
using namespace test_matrices;
struct n2216 : test_registry_<__COUNTER__>::with<policies::n2216> {};
struct test_registry : test_registry_<__COUNTER__> {};
BOOST_OPENMETHOD_CLASSES(matrix, dense_matrix, n2216);
BOOST_OPENMETHOD_CLASSES(matrix, dense_matrix, test_registry);
BOOST_OPENMETHOD(
times, (virtual_<const matrix&>, virtual_<const matrix&>), string_pair,
n2216);
test_registry);
BOOST_OPENMETHOD_OVERRIDE(times, (const matrix&, const matrix&), string_pair) {
return string_pair(MATRIX_MATRIX, NONE);
@@ -87,10 +87,10 @@ BOOST_OPENMETHOD_OVERRIDE(
}
BOOST_AUTO_TEST_CASE(pick_any_ambiguous) {
auto compiler = n2216::initialize();
auto compiler = boost::openmethod::initialize<test_registry, n2216>();
BOOST_TEST(compiler.report.ambiguous == 1u);
// N2216: use covariant return types to resolve ambiguity.
// test_registry: use covariant return types to resolve ambiguity.
dense_matrix left, right;
auto result = times(left, right);
BOOST_TEST(result.first == MATRIX_DENSE);

View File

@@ -4,10 +4,10 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod.hpp>
#include <boost/openmethod/registry.hpp>
#include <boost/openmethod/preamble.hpp>
#include <boost/openmethod/policies/throw_error_handler.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include "test_util.hpp"
@@ -35,7 +35,7 @@ struct errors_ : test_registry_<N, capture_output> {
capture() {
prev = error_handler::set(
[this](const policies::default_error_handler::error_variant&
[this](const typename errors_::error_handler::error_variant&
error) {
prev(error);
std::visit([](auto&& arg) { throw arg; }, error);
@@ -51,13 +51,13 @@ struct errors_ : test_registry_<N, capture_output> {
return output::os.str();
}
policies::default_error_handler::function_type prev;
typename errors_::error_handler::function_type prev;
};
};
namespace TEST_NS {
using registry = errors_<__COUNTER__>;
struct registry : errors_<__COUNTER__> {};
BOOST_OPENMETHOD(
transpose, (virtual_ptr<const matrix, registry>), void, registry);
@@ -66,7 +66,7 @@ BOOST_OPENMETHOD_OVERRIDE(
transpose, (virtual_ptr<const diagonal_matrix, registry>), void) {
}
BOOST_AUTO_TEST_CASE(not_initialized) {
BOOST_AUTO_TEST_CASE(no_initialization) {
if constexpr (registry::has_runtime_checks) {
// throw during virtual_ptr construction, because of hash table lookup
{
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(not_initialized) {
BOOST_CHECK_THROW(
(unique_virtual_ptr<matrix, registry>{
std::make_unique<diagonal_matrix>()}),
not_initialized_error);
not_initialized);
BOOST_TEST(capture() == "not initialized\n");
}
@@ -83,12 +83,12 @@ BOOST_AUTO_TEST_CASE(not_initialized) {
registry::capture capture;
BOOST_CHECK_THROW(
transpose(make_unique_virtual<diagonal_matrix, registry>()),
not_initialized_error);
not_initialized);
}
} else {
try {
registry::check_initialized();
} catch (not_initialized_error&) {
registry::require_initialized();
} catch (not_initialized&) {
BOOST_TEST_FAIL("should have not thrown in release variant");
}
}
@@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(initialize_unknown_class) {
if constexpr (registry::has_runtime_checks) {
{
registry::capture capture;
BOOST_CHECK_THROW(registry::initialize(), unknown_class_error);
BOOST_CHECK_THROW(initialize<registry>(), missing_class);
BOOST_TEST(capture().find("unknown class") != std::string::npos);
}
}
@@ -136,10 +136,10 @@ BOOST_OPENMETHOD_OVERRIDE(transpose, (const matrix&), void) {
BOOST_AUTO_TEST_CASE(call_unknown_class) {
if constexpr (registry::has_runtime_checks) {
{
registry::initialize();
initialize<registry>();
registry::capture capture;
BOOST_CHECK_THROW(transpose(dense_matrix()), unknown_class_error);
BOOST_CHECK_THROW(transpose(dense_matrix()), missing_class);
BOOST_TEST(capture().find("unknown class") != std::string::npos);
}
}
@@ -165,21 +165,21 @@ BOOST_OPENMETHOD_OVERRIDE(
times, (const diagonal_matrix&, const matrix&), void) {
}
BOOST_AUTO_TEST_CASE(call_error) {
auto report = registry::initialize().report;
BOOST_AUTO_TEST_CASE(bad_call) {
auto report = initialize<registry>().report;
BOOST_TEST(report.not_implemented == 1u);
BOOST_TEST(report.ambiguous == 1u);
{
registry::capture capture;
BOOST_CHECK_THROW(times(matrix(), matrix()), not_implemented_error);
BOOST_CHECK_THROW(times(matrix(), matrix()), no_overrider);
BOOST_TEST(capture().find("not implemented") != std::string::npos);
}
{
registry::capture capture;
BOOST_CHECK_THROW(
times(diagonal_matrix(), diagonal_matrix()), ambiguous_error);
times(diagonal_matrix(), diagonal_matrix()), ambiguous_call);
BOOST_TEST(capture().find("ambiguous") != std::string::npos);
}
}
@@ -198,7 +198,7 @@ BOOST_OPENMETHOD(
times, (virtual_<const matrix&>, virtual_<const matrix&>), void, registry);
BOOST_AUTO_TEST_CASE(throw_error) {
registry::initialize();
initialize<registry>();
try {
times(matrix(), matrix());

View File

@@ -3,7 +3,7 @@
// See accompanying file LICENSE_1_0.txt
// or q at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>

View File

@@ -15,7 +15,7 @@ struct static_registry
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY static_registry
#include <boost/openmethod.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
struct Animal {};

View File

@@ -3,7 +3,7 @@
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>

View File

@@ -4,8 +4,8 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#include <iostream>
@@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
BOOST_OPENMETHOD_REGISTER(typename poke::template override<
poke_bear<virtual_ptr<Player, Registry>>>);
Registry::initialize();
initialize<Registry>();
using vptr_player = virtual_ptr<Player, Registry>;
static_assert(detail::is_virtual_ptr<vptr_player>);
@@ -248,13 +248,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
};
upcast::fn(virtual_bear_ptr);
// Registry::finalize();
// Registry::initialize();
// BOOST_TEST(
// (virtual_bear_ptr.vptr() == Registry::template static_vptr<Bear>) ==
// Registry::has_indirect_vptr);
}
} // namespace BOOST_OPENMETHOD_GENSYM
@@ -284,7 +277,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
virtual_ptr<Player, Registry>, virtual_ptr<Object, Registry>,
virtual_ptr<Player, Registry>>>);
Registry::initialize();
initialize<Registry>();
Bear bear;
BOOST_TEST(poke::fn(virtual_ptr<Player, Registry>(bear)) == "growl");
@@ -329,7 +322,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(
shared_virtual_ptr<Object, Registry>,
shared_virtual_ptr<Player, Registry>>>);
Registry::initialize();
initialize<Registry>();
auto bear = make_shared_virtual<Bear, Registry>();
auto warrior = make_shared_virtual<Warrior, Registry>();

View File

@@ -4,7 +4,7 @@
// or q at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/initialize.hpp>
#define BOOST_TEST_MODULE openmethod

View File

@@ -9,7 +9,7 @@
#include <boost/openmethod.hpp>
#include <boost/openmethod/policies/vptr_map.hpp>
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include "test_util.hpp"
@@ -42,7 +42,7 @@ void init_test() {
BOOST_OPENMETHOD_REGISTER(use_classes<Animal, Cat, Dog, Registry>);
struct id;
(void)&method<id, auto(virtual_ptr<Animal, Registry>)->void, Registry>::fn;
Registry::initialize();
initialize<Registry>();
}
struct direct_vector : test_registry_<__COUNTER__> {};
@@ -73,11 +73,6 @@ struct check_illegal_smart_ops {
!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Registry>, smart_ptr<const Animal>>);
// policies must be the same
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, debug_registry>,
virtual_ptr<smart_ptr<Animal>, release_registry>>);
// a smart virtual_ptr cannot be constructed from a plain reference or
// pointer
static_assert(!std::is_constructible_v<