after boost review

This commit is contained in:
Jean-Louis Leroy
2025-05-10 11:01:41 -04:00
parent ea7dbe86b5
commit 93e0388ab1
39 changed files with 12344 additions and 365 deletions

10
dev/local-flat.sh Executable file
View File

@@ -0,0 +1,10 @@
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/compiler.hpp
python3 dev/flatten.py \
flat/boost/openmethod/policies.hpp \
include/boost/openmethod/policies.hpp

View File

@@ -3,8 +3,6 @@
### Synopsis
Defined in <boost/openmethod/policies/basic_policy.hpp>.
```c++
namespace boost::openmethod {
@@ -18,14 +16,11 @@ struct basic_policy : abstract_policy, domain<Policy>, Facets... {
template<class NewPolicy>
using fork = /*unspecified*/;
template<class... MoreFacets>
using add = /*unspecified*/;
template<class... Facets>
using with = /*unspecified*/;
template<class Base, class Facet>
using replace = /*unspecified*/;
template<class Base>
using remove = /*unspecified*/;
template<class... Facets>
using without = /*unspecified*/;
};
struct release : basic_policy<release, ...> {};
@@ -43,6 +38,11 @@ using default_policy = policies::debug;
} // boost::openmethod
```
### Headers
Defined in <boost/openmethod/policies/basic_policy.hpp>. Also available via
`<boost/openmethod/core.hpp>` and `<boost/openmethod.hpp>`.
### Description
`basic_policy` implements a policy, which consists of a a collection of methods,
@@ -50,10 +50,8 @@ classes, dispatch data, and facets, which specify how to obtain a pointer to a
v-table from an object, how to report errors, whether to perform runtime sanity
checks, etc.
Some of these functionalities require static variables local to the policy.
Forthis reason, `basic_policy` uses the CRTP pattern to provide ensure that two
different policies - and the facets they contain - get their own copies of the
static state.
`basic_policy` has state. It uses the Curiously Recurring Template Pattern to
allow distinct policies to have distinct sets of static variables.
### Members
@@ -73,46 +71,56 @@ template<class NewPolicy>
using fork;
```
Creates a new policy from an existing one. _NewPolicy_, and the facets it
contains, do not share static variables with the original _Policy_. The new
policy does not retain any knowledge of the classes and methods registered in
the original.
Creates a new policy from an existing one. _NewPolicy_ does not share static
variables with the original _Policy_. The new policy does not retain any
knowledge of the classes and methods registered in the original.
#### add
`fork` forks the facets in the policy as well: any facet instantiated from a
class template is assumed to take a policy as its first template argument. The
template is re-instantiated with the new policy as the first arguments, while
the other arguments remain the same.
#### with
```c++
template<class... MoreFacets>
using add;
template<class... Facets>
using with;
```
Creates a new policy by adding _MoreFacets_ to the original policy's collection
of facets. The original policy and the new one share static variables.
Requires:: _Facets_ is a list of classes that derive from `facet`.
#### replace
Returns:: A new policy containing _Facets_, and the facets from the original
that do not have the same category as _Facets_.
Examples::
* `struct dyn_load : default_policy::fork<dyn_load>::with<indirect_vptr> {};` +
Creates a policy just like `default_policy`, with an extra indirection added
to the v-table pointers. This policy is suitable for use with dynamic loading.
* `struct release_with_diags : release::fork<release_with_diags>::with<basic_error_output<release_with_diags>> {};` +
Creates a policy just like `release`, except that it prints a diagnostic
message before terminating with `abort()`.
* `struct default_throw : default_policy::fork<default_throw>::with<throw_error_handler> {};` +
Creates a policy just like `default_policy`, except that it reports errors by
throwing exceptions, instead of calling a `std::function` like the default
error handler does.
#### without
```c++
template<class Base, class NewFacet>
using replace;
template<class... Facets>
using without;
```
Creates a new policy by replacing the facet in _Policy_ that derives from _Base_
with _NewFacet_. It is not an error if _policy_ does not contain such a facet;
in that case, the new policy contains the same facet as the original one.
Requires:: _Facets_ is a list of facet categories.
The original policy and the new one share static variables.
Returns:: A new policy containing the facets from the original that do not have
the same category as _Facets_.
#### remove
```c++
template<class Base>
using remove;
```
Creates a new policy by removing the facet in _Policy_ that derives from _Base_.
It is not an error if _policy_ does not contain such a facet; in that case, the
new policy contains the same facet as the original one.
The original policy and the new one share static variables.
Examples::
* `struct use_map : default_policy::fork<use_map>::with<vptr_map<use_map>>::without<type_hash> {};` +
Creates a policy just like `default_policy`, except that it stores pointers to
v-table in a `std::unordered_map`. Also removes the hash function, since it
will not be used.
### Non-members
@@ -122,8 +130,8 @@ The original policy and the new one share static variables.
struct release;
```
A policy that contains facet implementations `std_rtti`, `fast_perfect_hash`,
`vptr_vector` and `vectored_error_handler`.
A policy that contains facets `std_rtti`, `fast_perfect_hash`, `vptr_vector` and
`vectored_error_handler`.
#### debug

View File

@@ -27,10 +27,9 @@ include::{examplesdir}/dl.hpp[tag=header]
NOTE: The policy must be passed to the method as well as the
`virtual_ptr`{empty}s.
The `indirect_vptr` facet tells `virtual_ptr` to use a _reference_ to the vptr,
instead of its value. Even tough the value of the vptr changes when `initialize`
is called, the vptrs are stored in the same place (the policy's
`static_vptr<Class>` variables).
The `indirect_vptr` facet tells `virtual_ptr` to use a pointer to the vptr. Even
tough the value of the vptr changes when `initialize` is called, the vptrs are
stored in the same place (the policy's `static_vptr<Class>` variables).
We can now register the classes and and provide an overrider:
@@ -39,10 +38,6 @@ We can now register the classes and and provide an overrider:
include::{examplesdir}/dl_main.cpp[tag=main]
----
It is not necessary to pass the policy to `BOOST_OPENMETHOD_CLASSES`, because
`indirect_vptr` does not have any state. As for `BOOST_OPENMETHOD_OVERRIDE`, it
deduces the policy from the method.
At this point we only have one overrider. Animals of all species ignore one
another:

View File

@@ -6,17 +6,14 @@
Defined in <boost/openmethod/policies/basic_policy.hpp>.
```c++
namespace boost::openmethod::policies {
namespace boost::openmethod {
namespace policies {
struct error_handler;
}
```
Defined in <boost/openmethod/types.hpp>.
```c++
namespace boost::openmethod {
struct openmethod_error {};
@@ -40,7 +37,7 @@ struct type_mismatch_error : openmethod_error {
type_id type;
};
} // boost::openmethod::policies
}
```
### Description

View File

@@ -23,7 +23,7 @@ include::{examplesdir}/friendship.cpp[tag=friend_all]
Be aware, though, that the overriders of _any_ method called `poke` - with any
signature - are granted friendship.
We can also befriendto individual overriders:
We can also befriend individual overriders:
[source,c++]
----

View File

@@ -2,9 +2,9 @@
## Headers and Namespaces
Most real-life programs will be organized in multiple files and multiple
namespaces. OpenMethod interacts with headers and namespaces very naturally,
unless using-directives are avoided. In that case, there are a few things to be
aware of.
namespaces. OpenMethod interacts with headers and namespaces naturally, if
using-directives are avoided. In that case, there are a few things to be aware
of.
Let's break the Animals example into headers and namespaces. First we put
`Animal` in its own header and namespace:
@@ -27,7 +27,7 @@ it is "called" only in a non-evaluated context.
* It defines an inline function with the same name and signature as the
method (with the `virtual_` decorators stripped).
Next, let's implement the `Cat` class, and a derived class, `Chhetah`, in the
Next, let's implement the `Cat` class, and a derived class, `Cheetah`, in the
`felines` namespace:
[source,c++]
@@ -51,7 +51,7 @@ in scope, or with qualified names.
as the overrider itself. It "calls" the guide function in a non-evaluated
context, passing it a `std::ostream&` and a `virtual_ptr<Cat>`. The return type
of the guide function is the method to add the overrider to. Exactly one guide
function must match. The normal rules of overload resolution apply. In this
function must match. The normal rules of overload resolution apply. In that
case, the guide function is found via argument dependant lookup (ADL).
The macro adds several constructs to the current namespace:
@@ -140,7 +140,7 @@ BOOST_OPENMETHOD_OVERRIDE(
...will fail to compile, with an error like "reference to
'poke_boost_openmethod_overriders' is ambiguous". That is because the overrider
containers exist in both the canides and felides namespaces, with the same name.
containers exist in both the canines and felines namespaces, with the same name.
Finally, the names passed as first arguments to the BOOST_OPENMETHOD and
BOOST_OPENMETHOD_OVERRIDE macros must be identifiers. Qualified names are not

View File

@@ -96,8 +96,3 @@ reference, because `virtual_ptr` has a conversion constructor for that:
----
include::{examplesdir}/hello_world.cpp[tag=call]
----
NOTE: `virtual_ptr` is more like a reference than a pointer: it cannot be null,
and it cannot be re-assigned. The only reason why it is not called `virtual_ref`
is to save the name in case it becomes possible to overload the dot operator in
future versions of C++.

6348
doc/html/index.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ Defined in `<boost/openmethod/compiler.hpp>`.
namespace boost::openmethod {
template<class Policy = BOOST_OPENMETHOD_DEFAULT_POLICY>
auto compiler<Policy>::initialize() -> /*unspecified*/;
auto initialize() -> /*unspecified*/;
}
```

View File

@@ -10,26 +10,27 @@ Class templates `use_classes`, `method`, `virtual_ptr`, and macros
argument, a policy class, which defaults to `policies::debug` in debug builds,
and `policies::release` in release builds.
A policy has a collection of _facets_. Facets control how type information is
obtained, how vptrs are fetched, how errors are handled and printed, etc. Some
are used in `initialize` and method dispatch; some are used by other facets in
the same policy as part of their implementation. See the reference for a list of
facets and stock implementations. Policies and facets are placed in the
`boost::openmethod::policies` namespace. Two policies are provided by the
A policy has a collection of _facets_. Each facet belongs to a facet category. A
policy may contain at most one facet of a given category. Facets control how
type information is obtained, how vptrs are fetched, how errors are handled and
printed, etc. Some are used in `initialize` and method dispatch; some are used
by other facets in the same policy as part of their implementation. See the
reference for the list of facets. Policies and facets are placed in the
`boost::openmethod::policies` namespace. Two stock policies are provided by the
library: `release` and `debug`.
`release` contains the following facets:
The `release` policy contains the following facets:
[cols="1,1,1"]
|===
|facet |implementations |role
|facet category |facet |role
| rtti
| std_rtti, minimal_rtti
| std_rtti
| provides type information for classes and objects
| extern_vptr
| vptr_vector, vptr_map
| vptr_vector
| stores vptrs in an indexed collection
| type_hash
@@ -37,16 +38,16 @@ library: `release` and `debug`.
| hash type id to an index in a vector
| error_handler
| vectored_error_handler, throw_error_handler
| vectored_error_handler
| handles errors
|===
`policies::debug` contains the same facets as `release`, plus a few more:
The `debug` policy contains the same facets as `release`, plus a few more:
[cols="1,1,1"]
|===
|facet |implementation |role
|facet category |facet |role
| runtime_checks
| (itself)
@@ -65,16 +66,18 @@ library: `release` and `debug`.
Policies, and some facets, have static variables. When it is the case, they are
implemented as CRTP classes.
Policies can be created from scratch, using the `basic_policy` template, or by
adding or removing facets from existing policies. For example, `policies::debug`
is a tweak of `policies::release`:
Policies can be created from scratch, using the `basic_policy` template, or
constructed from existing policies by adding and removing facets. For example,
`policies::debug` is a tweak of `policies::release`:
[source,c++]
----
namespace boost::openmethod::policies {
struct debug : release::add<
runtime_checks, basic_error_output<debug>, basic_trace_output<debug>> {};
struct debug : release::fork<debug>::with<
runtime_checks, basic_error_output<debug>,
basic_trace_output<debug>> {};
}
----
@@ -85,4 +88,4 @@ be overriden by defining the macroprocessor symbol
`<boost/openmethod/core.hpp>`. The value of the symbol is used as a default
template parameter for `use_classes`, `method`, `virtual_ptr`, and others. Once
the `core` header has been included, changing `BOOST_OPENMETHOD_DEFAULT_POLICY`
has no effect. See below for examples.
has no effect.

View File

@@ -8,8 +8,8 @@ namespace boost::openmethod::policies {
### Synopsis
template<class Policy, class Facet = void, class Map = /* unspecified */>
class vptr_map : extern_vptr, Facet /* if not void */ {
template<class Policy, class MapAdaptor = mp11::mp_quote<std::unordered_map>>
class vptr_map : public extern_vptr {
public:
template<typename ForwardIterator>
static auto register_vptrs(ForwardIterator first, ForwardIterator last) -> void;
@@ -24,16 +24,12 @@ class vptr_map : extern_vptr, Facet /* if not void */ {
### Description
`vptr_map` is an implementation of `external_vptr that stores the pointers to
the v-tables in a map.
the v-tables in a map. If `Policy` contains `indirect_vptr`, a level of
indirection is added, making the policy usable in presence of dynamic loading.
`Policy` is the policy containing the facet.
If `Facet` is specified, it must be either `void` or `indirect_vptr`. `void`
indicates that straight pointers to v-tables should be stored.
`Map` is an `AssociativeContainer` that maps `type_id`{empty}s to
`vptr_type`{empty}s if `Facet` is `void`, or pointers `vptr_type`{empty}s if
`Facet` is `indirect_vptr`.
`MapAdaptor` is a Boost.Mp11 quoted metafunction that returns a map type.
### Members

View File

@@ -8,7 +8,7 @@ Defined in <boost/openmethod/policies/vptr_vector.hpp>.
```c++
namespace boost::openmethod::policies {
template<class Policy, typename Facet = void>
template<class Policy>
class vptr_vector : Base {
public:
template<typename ForwardIterator>
@@ -23,12 +23,10 @@ class vptr_vector : Base {
### Description
`vptr_vector` is an implementation or `external_vptr` that keeps the pointers
to the v-tables in a `std::vector`. If `UseIndirectVptrs` is `indirect_vptr`,
stores pointers to pointers to the v-tables.
If `Facet` is specified, it must be either `void` or `indirect_vptr`. `void`
indicates that straight pointers to v-tables should be stored.
`vptr_vector` is an implementation or `external_vptr` that keeps the pointers to
the v-tables in a `std::vector`. If `Policy` contains `indirect_vptr`, a level
of indirection is added, making the policy usable in presence of dynamic
loading.
`Policy` is the policy containing the facet.

View File

@@ -3,7 +3,7 @@
### Synopsis
Defined in <boost/openmethod/core.hpp>.
Defined in <boost/openmethod/with_vptr.hpp>.
```c++
namespace boost::openmethod {

View File

@@ -78,6 +78,9 @@ struct Dog : virtual Animal {
namespace bom = boost::openmethod;
struct custom_rtti : bom::policies::rtti {
template<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
template<typename T>
static auto static_type() -> bom::type_id {
if constexpr (std::is_base_of_v<Animal, T>) {

View File

@@ -27,10 +27,9 @@ struct Cow : Herbivore {};
struct Wolf : Carnivore {};
struct dynamic_policy
: boost::openmethod::default_policy::fork<dynamic_policy>::replace<
boost::openmethod::policies::extern_vptr,
boost::openmethod::policies::vptr_vector<
dynamic_policy, boost::openmethod::policies::indirect_vptr>> {};
: boost::openmethod::default_policy::fork<dynamic_policy>::with<
boost::openmethod::policies::indirect_vptr> {};
template<class Class>
using dyn_vptr = boost::openmethod::virtual_ptr<Class, dynamic_policy>;

View File

@@ -26,9 +26,8 @@ struct throw_if_not_implemented : bom::policies::error_handler {
}
};
struct throwing_policy
: bom::default_policy::fork<throwing_policy>::replace<
bom::policies::error_handler, throw_if_not_implemented> {};
struct throwing_policy : bom::default_policy::fork<throwing_policy>::with<
throw_if_not_implemented> {};
#define BOOST_OPENMETHOD_DEFAULT_POLICY throwing_policy

4365
flat/boost/openmethod.hpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -262,15 +262,6 @@ struct compiler : detail::generic_compiler {
-> bool;
static auto is_base(const overrider* a, const overrider* b) -> bool;
static auto static_type(type_id type) -> type_id {
if constexpr (std::is_base_of_v<
policies::deferred_static_rtti, policies::rtti>) {
return reinterpret_cast<type_id (*)()>(type)();
} else {
return type;
}
}
mutable detail::trace_type<Policy> trace;
static constexpr bool trace_enabled =
Policy::template has_facet<policies::trace_output>;
@@ -342,7 +333,6 @@ void compiler<Policy>::resolve_static_type_ids() {
for (auto& ti : range{method.vp_begin, method.vp_end}) {
if (*method.vp_end == 0) {
resolve(&ti);
*method.vp_end = 1;
}
for (auto& overrider : method.specs) {
@@ -356,6 +346,8 @@ void compiler<Policy>::resolve_static_type_ids() {
}
}
}
*method.vp_end = 1;
}
}
}
@@ -693,7 +685,7 @@ void compiler<Policy>::assign_slots() {
auto first_slot = cls.used_slots.find_first();
cls.first_slot =
first_slot == boost::dynamic_bitset<>::npos ? 0 : first_slot;
first_slot == boost::dynamic_bitset<>::npos ? 0u : first_slot;
cls.vtbl.resize(cls.used_slots.size() - cls.first_slot);
++trace << cls << " vtbl: " << cls.first_slot << "-"
<< cls.used_slots.size() << " slots " << cls.used_slots

View File

@@ -351,7 +351,9 @@ class virtual_ptr_impl {
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<Class*, Other*>>>
typename = std::enable_if_t<
std::is_constructible_v<Class*, Other*> &&
Policy::template is_polymorphic<Class>>>
virtual_ptr_impl(Other& other)
: vp(box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(other))),
obj(&other) {
@@ -359,9 +361,11 @@ class virtual_ptr_impl {
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<
Class*,
decltype(std::declval<virtual_ptr<Other, Policy>>().get())>>>
typename = std::enable_if_t<
std::is_constructible_v<
Class*,
decltype(std::declval<virtual_ptr<Other, Policy>>().get())> &&
Policy::template is_polymorphic<Class>>>
virtual_ptr_impl(Other* other)
: vp(box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(*other))),
obj(other) {
@@ -381,7 +385,7 @@ class virtual_ptr_impl {
typename = std::enable_if_t<std::is_constructible_v<
Class*,
decltype(std::declval<virtual_ptr<Other, Policy>>().get())>>>
virtual_ptr_impl(virtual_ptr<Other, Policy>& other)
virtual_ptr_impl(virtual_ptr_impl<Other, Policy>& other)
: vp(other.vp), obj(other.get()) {
// Why is this needed? Consider this conversion conversion from
// smart to dumb pointer:
@@ -402,7 +406,9 @@ class virtual_ptr_impl {
template<
class Other,
typename = std::enable_if_t<std::is_assignable_v<Class*, Other*>>>
typename = std::enable_if_t<
std::is_assignable_v<Class*, Other*> &&
Policy::template is_polymorphic<Class>>>
virtual_ptr_impl& operator=(Other& other) {
obj = &other;
vp = box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(other));
@@ -411,7 +417,9 @@ class virtual_ptr_impl {
template<
class Other,
typename = std::enable_if_t<std::is_assignable_v<Class*, Other*>>>
typename = std::enable_if_t<
std::is_assignable_v<Class*, Other*> &&
Policy::template is_polymorphic<Class>>>
virtual_ptr_impl& operator=(Other* other) {
obj = other;
vp = box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(*other));
@@ -524,7 +532,8 @@ class virtual_ptr_impl<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, const Other&>>>
std::is_constructible_v<Class, const Other&> &&
Policy::template is_polymorphic<element_type>>>
virtual_ptr_impl(const Other& other)
: vp(box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr)),
@@ -535,18 +544,20 @@ class virtual_ptr_impl<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, Other&>>>
std::is_constructible_v<Class, Other&> &&
Policy::template is_polymorphic<element_type>>>
virtual_ptr_impl(Other& other)
: vp(box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr)),
obj(other) {
other ? Policy::dynamic_vptr(*other) : null_vptr)),
obj(other) {
}
template<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, Other&&>>>
std::is_constructible_v<Class, Other&&> &&
Policy::template is_polymorphic<element_type>>>
virtual_ptr_impl(Other&& other)
: vp(box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr)),
@@ -558,7 +569,7 @@ class virtual_ptr_impl<
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, const Other&>>>
virtual_ptr_impl(const virtual_ptr<Other, Policy>& other)
virtual_ptr_impl(const virtual_ptr_impl<Other, Policy>& other)
: vp(other.vp), obj(other.obj) {
}
@@ -567,7 +578,7 @@ class virtual_ptr_impl<
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, Other&>>>
virtual_ptr_impl(virtual_ptr<Other, Policy>& other)
virtual_ptr_impl(virtual_ptr_impl<Other, Policy>& other)
: vp(other.vp), obj(other.obj) {
}
@@ -581,7 +592,7 @@ class virtual_ptr_impl<
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, Other&&>>>
virtual_ptr_impl(virtual_ptr<Other, Policy>&& other)
virtual_ptr_impl(virtual_ptr_impl<Other, Policy>&& other)
: vp(other.vp), obj(std::move(other.obj)) {
other.vp = box_vptr<use_indirect_vptrs>(null_vptr);
}
@@ -596,7 +607,8 @@ class virtual_ptr_impl<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, const Other&>>>
std::is_assignable_v<Class, const Other&> &&
Policy::template is_polymorphic<element_type>>>
virtual_ptr_impl& operator=(const Other& other) {
obj = other;
vp = box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(*other));
@@ -607,7 +619,8 @@ class virtual_ptr_impl<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, Other&&>>>
std::is_assignable_v<Class, Other&&> &&
Policy::template is_polymorphic<element_type>>>
virtual_ptr_impl& operator=(Other&& other) {
vp = box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr);
@@ -620,12 +633,14 @@ class virtual_ptr_impl<
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, Other&>>>
virtual_ptr_impl& operator=(virtual_ptr<Other, Policy>& other) {
virtual_ptr_impl& operator=(virtual_ptr_impl<Other, Policy>& other) {
obj = other.obj;
vp = other.vp;
return *this;
}
virtual_ptr_impl& operator=(const virtual_ptr_impl& other) = default;
template<
class Other,
typename = std::enable_if_t<
@@ -1042,6 +1057,21 @@ class method<Name(Parameters...), ReturnType, Policy>
};
};
// Following cannot be `inline static` becaused of MSVC (19.43) bug causing a
// "no appropriate default constructor available". Try this in CE:
//
// template<typename>
// class method {
// method();
// method(const method&) = delete;
// method(method&&) = delete;
// ~method();
// public:
// static inline method instance;
// };
// template method<void>;
// https://godbolt.org/z/GzEn486P7
template<
typename Name, typename... Parameters, typename ReturnType, class Policy>
method<Name(Parameters...), ReturnType, Policy>
@@ -1168,13 +1198,7 @@ method<Name(Parameters...), ReturnType, Policy>::resolve_uni(
using namespace boost::mp11;
if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
vptr_type vtbl;
if constexpr (is_virtual_ptr<ArgType>) {
vtbl = arg.vptr();
} else {
vtbl = vptr<ArgType>(arg);
}
vptr_type vtbl = vptr<ArgType>(arg);
if constexpr (has_static_offsets<method>::value) {
if constexpr (Policy::template has_facet<
@@ -1203,14 +1227,7 @@ method<Name(Parameters...), ReturnType, Policy>::resolve_multi_first(
using namespace boost::mp11;
if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
vptr_type vtbl;
if constexpr (is_virtual_ptr<ArgType>) {
vtbl = arg.vptr();
} else {
vtbl = vptr<ArgType>(arg);
}
vptr_type vtbl = vptr<ArgType>(arg);
std::size_t slot;
if constexpr (has_static_offsets<method>::value) {
@@ -1251,14 +1268,7 @@ method<Name(Parameters...), ReturnType, Policy>::resolve_multi_next(
using namespace boost::mp11;
if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
vptr_type vtbl;
if constexpr (is_virtual_ptr<ArgType>) {
vtbl = arg.vptr();
} else {
vtbl = vptr<ArgType>(arg);
}
vptr_type vtbl = vptr<ArgType>(arg);
std::size_t slot, stride;
if constexpr (has_static_offsets<method>::value) {

View File

@@ -22,10 +22,9 @@ struct release : basic_policy<
release, std_rtti, fast_perfect_hash<release>,
vptr_vector<release>, vectored_error_handler<release>> {};
struct debug : release::add<
struct debug : release::fork<debug>::with<
runtime_checks, basic_error_output<debug>,
basic_trace_output<debug>>::
replace<error_handler, vectored_error_handler<debug>> {};
basic_trace_output<debug>> {};
} // namespace policies

View File

@@ -12,13 +12,10 @@
namespace boost::openmethod::policies {
template<class Policy, typename Stream = detail::ostderr>
struct basic_error_output : virtual error_output {
static Stream error_stream;
struct basic_error_output : error_output {
inline static Stream error_stream;
};
template<class Policy, typename Stream>
Stream basic_error_output<Policy, Stream>::error_stream;
} // namespace boost::openmethod::policies
#endif

View File

@@ -16,11 +16,25 @@
namespace boost::openmethod {
namespace policies {
struct facet {
static auto finalize() -> void {
}
};
} // namespace policies
namespace detail {
using class_catalog = detail::static_list<detail::class_info>;
using method_catalog = detail::static_list<detail::method_info>;
template<class Facet>
struct basic_facet : policies::facet {
using facet_type = Facet;
};
template<typename Policy, class Facet>
struct fork_facet {
using type = Facet;
@@ -33,6 +47,46 @@ struct fork_facet<NewPolicy, GenericFacet<OldPolicy, Args...>> {
using type = GenericFacet<NewPolicy, Args...>;
};
template<class Facets, class...>
struct with_aux;
template<class Facets>
struct with_aux<Facets> {
using type = Facets;
};
template<class Facets, class Facet, class... MoreFacets>
struct with_aux<Facets, Facet, MoreFacets...> {
using replace = mp11::mp_replace_if_q<
Facets,
mp11::mp_bind_front_q<
mp11::mp_quote_trait<std::is_base_of>, typename Facet::facet_type>,
Facet>;
using replace_or_add = std::conditional_t<
std::is_same_v<replace, Facets>, mp11::mp_push_back<Facets, Facet>,
replace>;
using type = typename with_aux<replace_or_add, MoreFacets...>::type;
};
template<class Facets, class...>
struct without_aux;
template<class Facets>
struct without_aux<Facets> {
using type = Facets;
};
template<class Facets, class Facet, class... MoreFacets>
struct without_aux<Facets, Facet, MoreFacets...> {
using type = typename without_aux<
mp11::mp_remove_if_q<
Facets,
mp11::mp_bind_front_q<
mp11::mp_quote_trait<std::is_base_of>,
typename Facet::facet_type>>,
MoreFacets...>::type;
};
} // namespace detail
namespace policies {
@@ -42,12 +96,7 @@ struct abstract_policy {};
// -----------------------------------------------------------------------------
// Facets
struct facet {
static auto finalize() -> void {
}
};
struct rtti : facet {
struct rtti : detail::basic_facet<rtti> {
static auto type_index(type_id type) -> type_id {
return type;
}
@@ -59,39 +108,26 @@ struct rtti : facet {
};
struct deferred_static_rtti : rtti {};
struct error_handler : facet {};
struct type_hash : facet {};
struct extern_vptr : facet {};
struct indirect_vptr : facet {};
struct error_output : facet {};
struct trace_output : facet {};
struct runtime_checks : facet {};
struct error_handler : detail::basic_facet<error_handler> {};
struct type_hash : detail::basic_facet<type_hash> {};
struct extern_vptr : detail::basic_facet<extern_vptr> {};
struct indirect_vptr : detail::basic_facet<indirect_vptr> {};
struct error_output : detail::basic_facet<error_output> {};
struct trace_output : detail::basic_facet<trace_output> {};
struct runtime_checks : detail::basic_facet<runtime_checks> {};
// -----------------------------------------------------------------------------
// domain
template<class Policy>
struct domain {
static detail::class_catalog classes;
static detail::method_catalog methods;
inline static detail::class_catalog classes;
inline static detail::method_catalog methods;
template<class Class>
static vptr_type static_vptr;
static std::vector<std::uintptr_t> dispatch_data;
inline static vptr_type static_vptr;
inline static std::vector<std::uintptr_t> dispatch_data;
};
template<class Policy>
detail::class_catalog domain<Policy>::classes;
template<class Policy>
detail::method_catalog domain<Policy>::methods;
template<class Policy>
template<class Class>
vptr_type domain<Policy>::static_vptr;
template<class Policy>
std::vector<std::uintptr_t> domain<Policy>::dispatch_data;
template<class Policy, class... Facets>
struct basic_policy : abstract_policy, domain<Policy>, Facets... {
using facets = mp11::mp_list<Facets...>;
@@ -103,28 +139,17 @@ struct basic_policy : abstract_policy, domain<Policy>, Facets... {
using fork = basic_policy<
NewPolicy, typename detail::fork_facet<NewPolicy, Facets>::type...>;
template<class... MoreFacets>
using add = basic_policy<Policy, Facets..., MoreFacets...>;
template<class Base, class Facet>
using replace = boost::mp11::mp_apply<
template<class... NewFacets>
using with = boost::mp11::mp_apply<
basic_policy,
boost::mp11::mp_push_front<
boost::mp11::mp_replace_if_q<
facets,
boost::mp11::mp_bind_front_q<
boost::mp11::mp_quote_trait<std::is_base_of>, Base>,
Facet>,
Policy>>;
typename detail::with_aux<facets, NewFacets...>::type, Policy>>;
template<class Base>
using remove = boost::mp11::mp_apply<
template<class... RemoveFacets>
using without = boost::mp11::mp_apply<
basic_policy,
boost::mp11::mp_push_front<
boost::mp11::mp_remove_if_q<
facets,
boost::mp11::mp_bind_front_q<
boost::mp11::mp_quote_trait<std::is_base_of>, Base>>,
typename detail::without_aux<facets, RemoveFacets...>::type,
Policy>>;
};

View File

@@ -19,19 +19,13 @@ namespace boost::openmethod::policies {
template<class Policy, typename Stream = detail::ostderr>
struct basic_trace_output : virtual trace_output {
static bool trace_enabled;
static Stream trace_stream;
inline static bool trace_enabled = []() {
auto env = getenv("BOOST_OPENMETHOD_TRACE");
return env && *env++ == '1' && *env++ == 0;
}();
inline static Stream trace_stream;
};
template<class Policy, typename Stream>
Stream basic_trace_output<Policy, Stream>::trace_stream;
template<class Policy, typename Stream>
bool basic_trace_output<Policy, Stream>::trace_enabled([]() {
auto env = getenv("BOOST_OPENMETHOD_TRACE");
return env && *env++ == '1' && *env++ == 0;
}());
} // namespace boost::openmethod::policies
#ifdef _MSC_VER

View File

@@ -8,6 +8,7 @@
#include <boost/openmethod/policies/basic_policy.hpp>
#include <limits>
#include <random>
namespace boost::openmethod {
@@ -24,12 +25,11 @@ namespace policies {
template<class Policy>
class fast_perfect_hash : public type_hash {
static type_id hash_mult;
static std::size_t hash_shift;
static std::size_t hash_min;
static std::size_t hash_max;
static void check(std::size_t index, type_id type);
inline static type_id hash_mult;
inline static std::size_t hash_shift;
inline static std::size_t hash_min;
inline static std::size_t hash_max;
inline static void check(std::size_t index, type_id type);
public:
struct report {
@@ -100,6 +100,8 @@ void fast_perfect_hash<Policy>::hash_initialize(
for (std::size_t pass = 0; pass < 4; ++pass, ++M) {
hash_shift = 8 * sizeof(type_id) - M;
auto hash_size = 1 << M;
hash_min = (std::numeric_limits<std::size_t>::max)();
hash_max = (std::numeric_limits<std::size_t>::min)();
if constexpr (trace_enabled) {
if (Policy::trace_enabled) {
@@ -108,15 +110,13 @@ void fast_perfect_hash<Policy>::hash_initialize(
}
}
bool found = false;
std::size_t attempts = 0;
buckets.resize(hash_size);
while (!found && attempts < 100000) {
while (attempts < 100000) {
std::fill(buckets.begin(), buckets.end(), static_cast<type_id>(-1));
++attempts;
++total_attempts;
found = true;
hash_mult = uniform_dist(rnd) | 1;
for (auto iter = first; iter != last; ++iter) {
@@ -128,16 +128,13 @@ void fast_perfect_hash<Policy>::hash_initialize(
hash_max = (std::max)(hash_max, index);
if (buckets[index] != static_cast<type_id>(-1)) {
found = false;
break;
goto collision;
}
buckets[index] = type;
}
}
}
if (found) {
if constexpr (trace_enabled) {
if (Policy::trace_enabled) {
Policy::trace_stream << " found " << hash_mult << " after "
@@ -148,6 +145,8 @@ void fast_perfect_hash<Policy>::hash_initialize(
}
return;
collision: {}
}
}
@@ -176,15 +175,6 @@ void fast_perfect_hash<Policy>::check(std::size_t index, type_id type) {
}
}
template<class Policy>
type_id fast_perfect_hash<Policy>::hash_mult;
template<class Policy>
std::size_t fast_perfect_hash<Policy>::hash_shift;
template<class Policy>
std::size_t fast_perfect_hash<Policy>::hash_min;
template<class Policy>
std::size_t fast_perfect_hash<Policy>::hash_max;
} // namespace policies
} // namespace boost::openmethod

View File

@@ -36,8 +36,6 @@ class vectored_error_handler : public error_handler {
}
private:
static function_type fn;
static auto default_handler(const error_variant& error_v) {
using namespace detail;
using namespace policies;
@@ -74,12 +72,13 @@ class vectored_error_handler : public error_handler {
}
}
}
static function_type fn; // Cannot be inline static because it confuses MSVC
};
template<class Policy>
typename vectored_error_handler<Policy>::function_type
vectored_error_handler<Policy>::fn =
vectored_error_handler<Policy>::default_handler;
vectored_error_handler<Policy>::fn = default_handler;
} // namespace boost::openmethod::policies

View File

@@ -10,30 +10,19 @@
#include <unordered_map>
namespace boost::openmethod::policies {
namespace boost::openmethod {
template<
class Policy, class Facet = void,
class Map = std::unordered_map<
type_id,
std::conditional_t<
std::is_same_v<Facet, indirect_vptr>, const vptr_type*, vptr_type>>>
class vptr_map : public extern_vptr,
public std::conditional_t<
std::is_same_v<Facet, void>, detail::empty, Facet> {
static_assert(
std::is_same_v<Facet, void> || std::is_same_v<Facet, indirect_vptr>);
static constexpr bool use_indirect_vptrs =
std::is_same_v<Facet, indirect_vptr>;
static_assert(
std::is_same_v<typename Map::mapped_type, vptr_type> ||
std::is_same_v<typename Map::mapped_type, const vptr_type*>);
static_assert(
std::is_same_v<typename Map::mapped_type, const vptr_type*> ==
use_indirect_vptrs);
namespace detail {
static Map vptrs;
template<class Policy, class MapAdaptor, typename Key, typename Value>
inline typename MapAdaptor::template fn<Key, Value> vptr_map_vptrs;
} // namespace detail
namespace policies {
template<class Policy, class MapAdaptor = mp11::mp_quote<std::unordered_map>>
class vptr_map : public extern_vptr {
public:
template<typename ForwardIterator>
static void register_vptrs(ForwardIterator first, ForwardIterator last) {
@@ -41,10 +30,14 @@ class vptr_map : public extern_vptr,
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
if constexpr (use_indirect_vptrs) {
vptrs[*type_iter] = &iter->vptr();
if constexpr (Policy::template has_facet<indirect_vptr>) {
detail::vptr_map_vptrs<Policy,
MapAdaptor, type_id,
const vptr_type*>.emplace(*type_iter, &iter->vptr());
} else {
vptrs[*type_iter] = iter->vptr();
detail::vptr_map_vptrs<
Policy, MapAdaptor, type_id, vptr_type
>.emplace(*type_iter, iter->vptr());
}
}
}
@@ -53,10 +46,16 @@ class vptr_map : public extern_vptr,
template<class Class>
static auto dynamic_vptr(const Class& arg) -> const vptr_type& {
auto type = Policy::dynamic_type(arg);
auto iter = vptrs.find(type);
bool constexpr use_indirect_vptrs =
Policy::template has_facet<indirect_vptr>;
const auto& map = detail::vptr_map_vptrs<
Policy, MapAdaptor, type_id,
std::conditional_t<
use_indirect_vptrs, const vptr_type*, vptr_type>>;
auto iter = map.find(type);
if constexpr (Policy::template has_facet<runtime_checks>) {
if (iter == vptrs.end()) {
if (iter == map.end()) {
if constexpr (Policy::template has_facet<error_handler>) {
unknown_class_error error;
error.type = type;
@@ -75,13 +74,14 @@ class vptr_map : public extern_vptr,
}
static auto finalize() -> void {
vptrs.clear();
detail::vptr_map_vptrs<
Policy, MapAdaptor, type_id, std::conditional_t<
Policy::template has_facet<indirect_vptr>,
const vptr_type*, vptr_type>>.clear();
}
};
template<class Policy, typename UseIndirectVptrs, class Map>
Map vptr_map<Policy, UseIndirectVptrs, Map>::vptrs;
} // namespace boost::openmethod::policies
} // namespace policies
} // namespace boost::openmethod
#endif

View File

@@ -11,20 +11,22 @@
#include <variant>
#include <vector>
namespace boost::openmethod::policies {
namespace boost::openmethod {
template<class Policy, typename Facet = void>
class vptr_vector : public extern_vptr,
public std::conditional_t<
std::is_same_v<Facet, void>, detail::empty, Facet> {
static_assert(
std::is_same_v<Facet, void> || std::is_same_v<Facet, indirect_vptr>);
static constexpr bool use_indirect_vptrs =
std::is_same_v<Facet, indirect_vptr>;
using element_type =
std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type>;
static std::vector<element_type> vptrs;
namespace detail {
template<class Policy>
inline std::vector<vptr_type> vptr_vector_vptrs;
template<class Policy>
inline std::vector<const vptr_type*> vptr_vector_indirect_vptrs;
} // namespace detail
namespace policies {
template<class Policy>
class vptr_vector : public extern_vptr {
public:
template<typename ForwardIterator>
static auto register_vptrs(ForwardIterator first, ForwardIterator last)
@@ -49,7 +51,11 @@ class vptr_vector : public extern_vptr,
++size;
}
vptrs.resize(size);
if constexpr (Policy::template has_facet<indirect_vptr>) {
detail::vptr_vector_indirect_vptrs<Policy>.resize(size);
} else {
detail::vptr_vector_vptrs<Policy>.resize(size);
}
for (auto iter = first; iter != last; ++iter) {
for (auto type_iter = iter->type_id_begin();
@@ -60,10 +66,11 @@ class vptr_vector : public extern_vptr,
index = Policy::hash_type_id(index);
}
if constexpr (use_indirect_vptrs) {
vptrs[index] = &iter->vptr();
if constexpr (Policy::template has_facet<indirect_vptr>) {
detail::vptr_vector_indirect_vptrs<Policy>[index] =
&iter->vptr();
} else {
vptrs[index] = iter->vptr();
detail::vptr_vector_vptrs<Policy>[index] = iter->vptr();
}
}
}
@@ -77,22 +84,23 @@ class vptr_vector : public extern_vptr,
index = Policy::hash_type_id(index);
}
if constexpr (use_indirect_vptrs) {
return *vptrs[index];
if constexpr (Policy::template has_facet<indirect_vptr>) {
return *detail::vptr_vector_indirect_vptrs<Policy>[index];
} else {
return vptrs[index];
return detail::vptr_vector_vptrs<Policy>[index];
}
}
static auto finalize() -> void {
vptrs.clear();
if constexpr (Policy::template has_facet<indirect_vptr>) {
detail::vptr_vector_indirect_vptrs<Policy>.clear();
} else {
detail::vptr_vector_vptrs<Policy>.clear();
}
}
};
template<class Policy, typename UseIndirectVptrs>
std::vector<typename vptr_vector<Policy, UseIndirectVptrs>::element_type>
vptr_vector<Policy, UseIndirectVptrs>::vptrs;
} // namespace boost::openmethod::policies
} // namespace policies
} // namespace boost::openmethod
#endif

View File

@@ -389,7 +389,6 @@ BOOST_OPENMETHOD_OVERRIDE(
return Types(DIAGONAL_MATRIX, next(a, b).first);
}
BOOST_AUTO_TEST_CASE(ambiguity) {
auto compiler = initialize<policy>();
BOOST_TEST(compiler.report.ambiguous == 1u);

View File

@@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(test_policy) {
{
// check that adding a facet keeps static data from original
struct policy : default_policy::add<policies::indirect_vptr> {};
struct policy : default_policy::with<policies::indirect_vptr> {};
BOOST_TEST(&policy::methods == &default_policy::methods);
BOOST_TEST(&policy::classes == &default_policy::classes);
BOOST_TEST(
@@ -301,18 +301,63 @@ using namespace policies;
struct key1;
struct key2;
struct alt_rtti {};
struct foo : detail::basic_facet<foo> {};
struct foo1 : foo {};
struct foo2 : foo {};
struct bar : detail::basic_facet<bar> {};
struct bar1 : bar {};
struct bar2 : bar {};
static_assert(
std::is_same_v<fork_facet<key2, domain<key1>>::type, domain<key2>>);
struct policy1 : basic_policy<policy1, std_rtti> {};
struct policy2 : policy1::fork<policy2> {};
struct policy3 : policy1::fork<policy3>::replace<std_rtti, alt_rtti> {};
static_assert(std::is_base_of_v<foo1::facet_type, foo>);
static_assert(std::is_base_of_v<bar1::facet_type, bar>);
static_assert(std::is_same_v<policy2::facets, mp11::mp_list<std_rtti>>);
static_assert(
std::is_same_v<
detail::with_aux<mp11::mp_list<>, foo1>::type, mp11::mp_list<foo1>>);
static_assert(std::is_same_v<policy3::facets, mp11::mp_list<alt_rtti>>);
static_assert(std::is_same_v<
detail::with_aux<mp11::mp_list<foo1>, foo2>::type,
mp11::mp_list<foo2>>);
static_assert(std::is_same_v<
detail::with_aux<mp11::mp_list<foo1, bar1>, foo2, bar2>::type,
mp11::mp_list<foo2, bar2>>);
static_assert(
std::is_same_v<basic_policy<key1>::with<foo1>, basic_policy<key1, foo1>>);
static_assert(std::is_same_v<
basic_policy<key1, foo1>::with<foo2>, basic_policy<key1, foo2>>);
template<class Policy, class... Facets>
constexpr bool has_facets = (... && Policy::template has_facet<Facets>) &&
mp11::mp_size<typename Policy::facets>::value == sizeof...(Facets);
static_assert(std::is_same_v<
basic_policy<key1, foo1, bar1>::with<foo2>::facets,
mp11::mp_list<foo2, bar1>>);
static_assert(std::is_same_v<
basic_policy<key1, foo1, bar1>::with<bar2>::facets,
mp11::mp_list<foo1, bar2>>);
static_assert(std::is_same_v<
basic_policy<key1, foo1, bar1>::with<foo2, bar2>::facets,
mp11::mp_list<foo2, bar2>>);
static_assert(
std::is_same_v<basic_policy<key1>::without<foo>::facets, mp11::mp_list<>>);
static_assert(std::is_same_v<
basic_policy<key1, foo1, bar1>::without<foo>::facets,
mp11::mp_list<bar1>>);
static_assert(std::is_same_v<
basic_policy<key1, foo1, bar1>::without<bar>::facets,
mp11::mp_list<foo1>>);
} // namespace facets

View File

@@ -71,9 +71,7 @@ struct custom_rtti : policies::rtti {
}
};
struct test_policy
: default_policy::fork<test_policy>::replace<policies::rtti, custom_rtti> {
};
struct test_policy : default_policy::fork<test_policy>::with<custom_rtti> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
@@ -164,9 +162,8 @@ struct custom_rtti : policies::rtti {
}
};
struct test_policy
: default_policy::fork<test_policy>::replace<
policies::rtti, custom_rtti>::remove<policies::type_hash> {};
struct test_policy : default_policy::fork<test_policy>::with<
custom_rtti>::without<policies::type_hash> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
@@ -320,9 +317,8 @@ struct custom_rtti : policies::rtti {
}
};
struct test_policy
: default_policy::fork<test_policy>::replace<
policies::rtti, custom_rtti>::remove<policies::type_hash> {};
struct test_policy : default_policy::fork<test_policy>::with<
custom_rtti>::without<policies::type_hash> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
@@ -460,9 +456,8 @@ struct custom_rtti : policies::deferred_static_rtti {
}
};
struct test_policy
: default_policy::fork<test_policy>::replace<
policies::rtti, custom_rtti>::remove<policies::type_hash> {};
struct test_policy : default_policy::fork<test_policy>::with<
custom_rtti>::without<policies::type_hash> {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, test_policy);
@@ -476,6 +471,14 @@ BOOST_OPENMETHOD_OVERRIDE(poke, (Cat & cat, std::ostream& os), void) {
os << cat.name << " hisses.";
}
BOOST_OPENMETHOD(
meet, (virtual_<Animal&>, virtual_<Animal&>, std::ostream&), void,
test_policy);
BOOST_OPENMETHOD_OVERRIDE(meet, (Dog&, Dog&, std::ostream& os), void) {
os << "Both wag tails.";
}
BOOST_AUTO_TEST_CASE(custom_rtti_deferred) {
initialize<test_policy>();
@@ -486,11 +489,18 @@ BOOST_AUTO_TEST_CASE(custom_rtti_deferred) {
poke(a, os);
BOOST_TEST(os.str() == "Snoopy barks.");
}
{
std::stringstream os;
poke(b, os);
BOOST_TEST(os.str() == "Sylvester hisses.");
}
{
std::stringstream os;
meet(a, a, os);
BOOST_TEST(os.str() == "Both wag tails.");
}
}
} // namespace defered_type_id

View File

@@ -9,7 +9,10 @@
#include <boost/openmethod/policies.hpp>
namespace bom = boost::openmethod;
struct test_policy : bom::default_policy::remove<bom::policies::extern_vptr> {};
struct test_policy : bom::default_policy::without<
bom::policies::extern_vptr, bom::policies::type_hash> {
};
#define BOOST_OPENMETHOD_DEFAULT_POLICY test_policy
#include <boost/openmethod.hpp>
@@ -84,8 +87,7 @@ DomesticCat::~DomesticCat() {
describe(*this, std::cout);
}
BOOST_OPENMETHOD_OVERRIDE(
speak, (const Animal&, std::ostream& os), void) {
BOOST_OPENMETHOD_OVERRIDE(speak, (const Animal&, std::ostream& os), void) {
os << "???\n";
}
@@ -168,7 +170,7 @@ BOOST_AUTO_TEST_CASE(intrusive_mode) {
}
}
struct indirect_policy : test_policy::add<bom::policies::indirect_vptr> {};
struct indirect_policy : test_policy::with<bom::policies::indirect_vptr> {};
struct Indirect : bom::with_vptr<Indirect, indirect_policy> {
using bom::with_vptr<Indirect, indirect_policy>::boost_openmethod_vptr;

View File

@@ -40,7 +40,7 @@ BOOST_OPENMETHOD(poke, (virtual_<interfaces::Animal&>), std::string);
namespace canis {
// implement 'poke' for dogs
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog &), std::string) {
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&), std::string) {
return "bark";
}
@@ -61,18 +61,15 @@ BOOST_OPENMETHOD_OVERRIDE(
return "ignore";
}
BOOST_OPENMETHOD_OVERRIDE(
meet, (canis::Dog &, canis::Dog&), std::string) {
BOOST_OPENMETHOD_OVERRIDE(meet, (canis::Dog&, canis::Dog&), std::string) {
return "wag tail";
}
BOOST_OPENMETHOD_OVERRIDE(
meet, (canis::Dog &, felis::Cat&), std::string) {
BOOST_OPENMETHOD_OVERRIDE(meet, (canis::Dog&, felis::Cat&), std::string) {
return "chase";
}
BOOST_OPENMETHOD_OVERRIDE(
meet, (felis::Cat &, canis::Dog&), std::string) {
BOOST_OPENMETHOD_OVERRIDE(meet, (felis::Cat&, canis::Dog&), std::string) {
return "run";
}

View File

@@ -25,7 +25,7 @@ BOOST_OPENMETHOD_CLASSES(Animal, Dog, Animal);
BOOST_OPENMETHOD(poke, (virtual_<Animal&>), std::string);
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog &), std::string) {
BOOST_OPENMETHOD_OVERRIDE(poke, (Dog&), std::string) {
return "bark";
}

View File

@@ -50,29 +50,25 @@ BOOST_OPENMETHOD_OVERRIDE(pay, (virtual_ptr<Manager> exec), double) {
}
BOOST_OPENMETHOD_OVERRIDE(
approve,
(virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
approve, (virtual_ptr<const Role>, virtual_ptr<const Expense>, double),
bool) {
return false;
}
BOOST_OPENMETHOD_OVERRIDE(
approve,
(virtual_ptr<const Employee>, virtual_ptr<const Public>, double),
approve, (virtual_ptr<const Employee>, virtual_ptr<const Public>, double),
bool) {
return true;
}
BOOST_OPENMETHOD_OVERRIDE(
approve,
(virtual_ptr<const Manager>, virtual_ptr<const Taxi>, double),
approve, (virtual_ptr<const Manager>, virtual_ptr<const Taxi>, double),
bool) {
return true;
}
BOOST_OPENMETHOD_OVERRIDE(
approve,
(virtual_ptr<const Founder>, virtual_ptr<const Expense>, double),
approve, (virtual_ptr<const Founder>, virtual_ptr<const Expense>, double),
bool) {
return true;
}

View File

@@ -269,3 +269,25 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(shared_virtual_ptr_value, Policy, test_policies) {
template struct check_illegal_smart_ops<
std::shared_ptr, std::unique_ptr, direct_vector_policy>;
// Cannot construct or assign a virtual_ptr from a non-polymorphic object.
static_assert(!construct_assign_ok<
virtual_ptr<std::shared_ptr<NonPolymorphic>>,
const std::shared_ptr<NonPolymorphic>&>);
static_assert(!construct_assign_ok<
virtual_ptr<std::shared_ptr<NonPolymorphic>>,
std::shared_ptr<NonPolymorphic>&>);
static_assert(!construct_assign_ok<
virtual_ptr<std::shared_ptr<NonPolymorphic>>,
std::shared_ptr<NonPolymorphic>&&>);
// OK from another virtual_ptr though, because it can be constructed using
// 'final'.
static_assert(std::is_assignable_v<
virtual_ptr<std::shared_ptr<NonPolymorphic>>,
const virtual_ptr<std::shared_ptr<NonPolymorphic>>&>);
static_assert(construct_assign_ok<
virtual_ptr<std::shared_ptr<NonPolymorphic>>,
virtual_ptr<std::shared_ptr<NonPolymorphic>>&>);
static_assert(construct_assign_ok<
virtual_ptr<std::shared_ptr<NonPolymorphic>>,
virtual_ptr<std::shared_ptr<NonPolymorphic>>&&>);

View File

@@ -254,3 +254,19 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(unique_virtual_ptr_value, Policy, test_policies) {
template struct check_illegal_smart_ops<
std::unique_ptr, std::shared_ptr, direct_vector_policy>;
// Cannot construct or assign a virtual_ptr from a non-polymorphic object.
static_assert(!construct_assign_ok<
virtual_ptr<std::unique_ptr<NonPolymorphic>>,
const std::unique_ptr<NonPolymorphic>&>);
static_assert(!construct_assign_ok<
virtual_ptr<std::unique_ptr<NonPolymorphic>>,
std::unique_ptr<NonPolymorphic>&>);
static_assert(!construct_assign_ok<
virtual_ptr<std::unique_ptr<NonPolymorphic>>,
std::unique_ptr<NonPolymorphic>&&>);
// OK to move from another virtual_ptr though, because it can be constructed
// using 'final'.
static_assert(construct_assign_ok<
virtual_ptr<std::unique_ptr<NonPolymorphic>>,
virtual_ptr<std::unique_ptr<NonPolymorphic>>&&>);

View File

@@ -314,3 +314,19 @@ BOOST_AUTO_TEST_CASE(virtual_ptr_final_error) {
}
}
}
// Cannot construct or assign a virtual_ptr from a non-polymorphic object.
static_assert(
!construct_assign_ok<virtual_ptr<NonPolymorphic>, const NonPolymorphic&>);
static_assert(
!construct_assign_ok<virtual_ptr<NonPolymorphic>, NonPolymorphic&>);
static_assert(
!construct_assign_ok<virtual_ptr<NonPolymorphic>, NonPolymorphic&&>);
static_assert(
!construct_assign_ok<virtual_ptr<NonPolymorphic>, const NonPolymorphic*>);
static_assert(
!construct_assign_ok<virtual_ptr<NonPolymorphic>, NonPolymorphic*>);
// OK from another virtual_ptr though, because it can be constructed using
// 'final'.
static_assert(construct_assign_ok<
virtual_ptr<NonPolymorphic>, virtual_ptr<NonPolymorphic>>);

View File

@@ -11,6 +11,8 @@
#include <boost/openmethod/compiler.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <utility>
using namespace boost::openmethod;
using namespace boost::openmethod::policies;
@@ -26,6 +28,12 @@ struct Cat : virtual Animal {};
struct Dog : Animal {};
template<class Left, class Right>
constexpr bool construct_assign_ok =
std::is_constructible_v<Left, Right> && std::is_assignable_v<Left, Right>;
struct NonPolymorphic {};
template<class Policy>
void init_test() {
BOOST_OPENMETHOD_REGISTER(use_classes<Animal, Cat, Dog, Policy>);
@@ -37,15 +45,13 @@ void init_test() {
struct direct_vector_policy : default_policy::fork<direct_vector_policy> {};
struct indirect_vector_policy
: default_policy::fork<indirect_vector_policy>::replace<
extern_vptr, vptr_vector<indirect_vector_policy, indirect_vptr>> {};
: default_policy::fork<indirect_vector_policy>::with<indirect_vptr> {};
struct direct_map_policy : default_policy::fork<direct_map_policy>::replace<
extern_vptr, vptr_map<direct_map_policy>> {};
struct direct_map_policy : default_policy::fork<direct_map_policy>::with<
vptr_map<direct_map_policy>> {};
struct indirect_map_policy
: default_policy::fork<indirect_map_policy>::replace<
extern_vptr, vptr_map<indirect_map_policy, indirect_vptr>> {};
: default_policy::fork<indirect_map_policy>::with<indirect_vptr> {};
using test_policies = boost::mp11::mp_list<
direct_vector_policy, indirect_vector_policy, direct_map_policy,
@@ -118,8 +124,4 @@ struct check_illegal_smart_ops {
Animal&>);
};
template<class Left, class Right>
constexpr bool construct_assign_ok =
std::is_constructible_v<Left, Right> && std::is_assignable_v<Left, Right>;
#endif // TEST_VIRTUAL_PTR_VALUE_SEMANTICS_HPP