make virtual_ptr more pointer-like

This commit is contained in:
Jean-Louis Leroy
2025-04-14 16:52:46 -04:00
parent 0423a3968e
commit 6f77e9ab4b
18 changed files with 1548 additions and 754 deletions

View File

@@ -19,6 +19,6 @@ struct virtual_;
Marks a formal parameter of a method as virtual. Requires a specialization of
`virtual_traits` for `T` and the `Policy` of the method. Specializations for
`T&`, `T&&`, `std::unique_ptr<T>`, `std::shared_ptr<T>` and `const
`T&`, `T&&`, `T*`, `std::unique_ptr<T>`, `std::shared_ptr<T>` and `const
std::shared_ptr<T>&` are provided. See the documentation of `virtual_traits` for
more information.

View File

@@ -17,10 +17,17 @@ class virtual_ptr {
static constexpr bool is_smart_ptr = /* see below */;
using element_type = /* see below */;
virtual_ptr();
virtual_ptr(nullptr_t);
template<class Other> virtual_ptr(Other& other);
template<class Other> virtual_ptr(const Other& other);
template<class Other> virtual_ptr(Other&& other);
virtual_ptr& operator =(nullptr_t);
template<class Other> virtual_ptr& operator =(Other& other);
template<class Other> virtual_ptr& operator =(const Other& other);
template<class Other> virtual_ptr& operator =(Other&& other);
template<class Other>
static auto final(Other&& obj);
@@ -96,12 +103,7 @@ They are provided for `std::unique_ptr` and `std::shared_ptr`.
A plain `virtual_ptr` can be constructed from a reference, a smart pointer, or
another `virtual_ptr`. A smart `virtual_ptr` can be constructed from a smart
pointer or from a smart `virtual_ptr`. Usual conversions - from derived to base,
and from non-const to const - are allowed.
`virtual_ptr` does not have a default constructor, nor a "null" state. In that
respect, it behaves more like a reference than a pointer. The only reason why it
is not called `virtual_ref` is to save the name for the day C++ will support
smart references.
and from non-const to const - are supported.
### Members
@@ -128,24 +130,63 @@ The class of the object pointed to.
[source,c++]
----
template<class Other> virtual_ptr(Other& other); // 1
template<class Other> virtual_ptr(const Other& other); // 2
template<class Other> virtual_ptr(Other&& other); // 3
virtual_ptr(); // 1
virtual_ptr(nullptr_t); // 2
template<class Other> virtual_ptr(Other& other); // 3
template<class Other> virtual_ptr(const Other& other); // 4
template<class Other> virtual_ptr(Other&& other); // 5
template<class Other> virtual_ptr(Other* other); // 6
----
(1), (2) If `virtual_ptr` uses a plain pointer, `other` must be a lvalue
reference to an object of a registered class, or to a `virtual_ptr` (plain or
smart). If `virtual_ptr` uses a smart pointer, `other` must be a reference to a smart
pointer, or a smart `virtual_ptr`.
(1) Default constructor. Sets the v-table pointer to `nullptr`. If `Class` is
_not_ a smart pointer, the value of object pointer is is undefined.
(3) Smart `virtual_ptr` only. Constructs a `virtual_ptr` from a smart pointer or
a smart `virtual_ptr`. The (smart) object pointer is moved from `other`.
(2) Sets both the object and v-table pointers to `nullptr`.
(3), (4) For plain `virtual_ptr`{empty}s, `other` must be either a lvalue
reference to an object of a registered class, or a `virtual_ptr` (plain or
smart). For smart `virtual_ptr`{empty}s, `other` must be a reference to a smart
pointer, or a reference to a smart `virtual_ptr`.
(5) Constructs a `virtual_ptr` from a smart pointer or a smart `virtual_ptr`.
The object pointer is moved from `other`.
(6) Constructs a `virtual_ptr` from a plain pointer. Available only for plain
`virtual_ptr`{empty}s.
If `other` is also a `virtual_ptr`, the v-table pointer is copied from it.
Otherwise, it is deduced from the object. The `Policy` must be the same for both
`virtual_ptr`{empty}s.
#### assignment operators
[source,c++]
----
virtual_ptr& operator =(nullptr_t); // 1
template<class Other> virtual_ptr& operator =(Other& other); // 2
template<class Other> virtual_ptr& operator =(const Other& other); // 3
template<class Other> virtual_ptr& operator =(Other&& other); // 4
template<class Other> virtual_ptr& operator =(Other* other); // 5
----
(1) Sets both the object and v-table pointers to `nullptr`.
(2), (3) For plain `virtual_ptr`{empty}s, `other` must be either a lvalue
reference to an object of a registered class, or a `virtual_ptr` (plain or
smart). For smart `virtual_ptr`{empty}s, `other` must be a reference to a smart
pointer, or a reference to a smart `virtual_ptr`.
(4) Moves `other` to this `virtual_ptr`. If `other` is a smart pointer or a
smart virtual pointer, the object pointer is moved from `other`.
(5) Sets the object pointer to `other`. Available only for plain
`virtual_ptr`{empty}s.
If `other` is also a `virtual_ptr`, the v-table pointer is copied from it.
Otherwise, it is deduced from the object. The `Policy` must be the same for both
`virtual_ptr`{empty}s.
#### final
```c++

View File

@@ -35,6 +35,7 @@ Specializations are provided for:
* `const virtual_ptr<T, Policy>&`
* `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>

View File

@@ -232,7 +232,7 @@ BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr<Number> expr), int) {
return expr->val;
}
BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr<const Times> expr), int) {
BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr<Times> expr), int) {
return value(expr->left) * value(expr->right);
}

View File

@@ -81,7 +81,7 @@ BOOST_OPENMETHOD_OVERRIDE(meet, (virtual_ptr<Cat> cat, virtual_ptr<Dog> dog, std
#include <string>
auto main() -> int {
// Initialise method dispatch tables.
// Initialize the dispatch tables.
boost::openmethod::initialize();
// Create a few objects.

View File

@@ -255,10 +255,25 @@ struct virtual_traits<T&&, Policy> {
}
};
// For covariant return types.
template<typename T, class Policy>
struct virtual_traits<T*, Policy> {
using virtual_type = std::remove_cv_t<T>;
static auto peek(T* arg) -> const T& {
return *arg;
}
template<typename D>
static auto cast(T* ptr) {
static_assert(
std::is_base_of_v<
virtual_type, std::remove_pointer_t<std::remove_cv_t<D>>>);
if constexpr (detail::requires_dynamic_cast<T*, D>) {
return dynamic_cast<D>(ptr);
} else {
return static_cast<D>(ptr);
}
}
};
template<class... Classes>
@@ -303,6 +318,25 @@ struct is_virtual_ptr_aux<const virtual_ptr<Class, Policy>&> : std::true_type {
template<typename T>
constexpr bool is_virtual_ptr = detail::is_virtual_ptr_aux<T>::value;
template<bool Indirect>
inline auto box_vptr(const vptr_type& vp) {
if constexpr (Indirect) {
return &vp;
} else {
return vp;
}
}
inline auto unbox_vptr(vptr_type vp) {
return vp;
}
inline auto unbox_vptr(const vptr_type* vpp) {
return *vpp;
}
inline vptr_type null_vptr = nullptr;
template<class Class, class Policy, typename = void>
class virtual_ptr_impl {
public:
@@ -313,17 +347,44 @@ class virtual_ptr_impl {
static constexpr bool use_indirect_vptrs =
Policy::template has_facet<policies::indirect_vptr>;
template<class Other>
virtual_ptr_impl(Other& other)
: obj(&other), vp(Policy::dynamic_vptr(other)) {
virtual_ptr_impl() = default;
explicit virtual_ptr_impl(std::nullptr_t)
: obj(nullptr), vp(box_vptr<use_indirect_vptrs>(null_vptr)) {
}
template<class Other>
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<Class*, Other*>>>
virtual_ptr_impl(Other& other)
: obj(&other),
vp(box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(other))) {
}
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<
Class*,
decltype(std::declval<virtual_ptr<Other, Policy>>().get())>>>
virtual_ptr_impl(Other* other)
: obj(other),
vp(box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(*other))) {
}
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<
Class*,
decltype(std::declval<virtual_ptr<Other, Policy>>().get())>>>
virtual_ptr_impl(const virtual_ptr<Other, Policy>& other)
: obj(other.get()), vp(other.vp) {
}
template<class Other>
template<
class Other,
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)
: obj(other.get()), vp(other.vp) {
// Why is this needed? Consider this conversion conversion from
@@ -336,8 +397,46 @@ class virtual_ptr_impl {
// that is incorrect.
}
template<class Other>
virtual_ptr_impl(Other& other, const vptr_type& vp) : obj(&other), vp(vp) {
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<Class*, Other*>>>
virtual_ptr_impl(Other& other, const vptr_type& vp)
: obj(&other), vp(box_vptr<use_indirect_vptrs>(vp)) {
}
template<
class Other,
typename = std::enable_if_t<std::is_assignable_v<Class*, Other*>>>
virtual_ptr_impl& operator=(Other& other) {
obj = &other;
vp = box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(other));
return *this;
}
template<
class Other,
typename = std::enable_if_t<std::is_assignable_v<Class*, Other*>>>
virtual_ptr_impl& operator=(Other* other) {
obj = other;
vp = box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(*other));
return *this;
}
template<
class Other,
typename = std::enable_if_t<std::is_assignable_v<
Class*,
decltype(std::declval<virtual_ptr<Other, Policy>>().get())>>>
virtual_ptr_impl& operator=(const virtual_ptr_impl<Other, Policy>& other) {
obj = other.get();
vp = other.vp;
return *this;
}
virtual_ptr_impl& operator=(std::nullptr_t) {
obj = nullptr;
vp = box_vptr<use_indirect_vptrs>(null_vptr);
return *this;
}
auto get() const -> Class* {
@@ -345,11 +444,11 @@ class virtual_ptr_impl {
}
auto operator->() const {
return obj;
return get();
}
auto operator*() const -> element_type& {
return *obj;
return *get();
}
auto pointer() const -> const Class*& {
@@ -362,46 +461,47 @@ class virtual_ptr_impl {
std::is_base_of_v<Class, Other> || std::is_base_of_v<Other, Class>);
return virtual_ptr<Other, Policy>(
traits::template cast<Other&>(*obj), vp);
traits::template cast<Other&>(*obj), unbox_vptr(vp));
}
template<class, class>
friend struct virtual_traits;
protected:
std::conditional_t<use_indirect_vptrs, const vptr_type&, vptr_type> vp;
std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type> vp;
Class* obj;
};
// SFINAE helper: defined only if Class and Other are both smart pointers of the
// same kind and that a Other* can be converted to a Class* (this takes
// inheritance and constness into account).
template<
class Class, class Other, class Policy,
class Rebind = typename virtual_traits<Class, Policy>::template rebind<
typename Other::element_type>,
class IsConvertible = typename std::is_convertible<
typename Other::element_type*, typename Class::element_type*>::type>
struct enable_if_compatible_smart_ptr;
template<class Class, class Other, class Policy, typename = void>
struct same_smart_ptr_aux : std::false_type {};
template<class Class, class Other, class Policy>
struct enable_if_compatible_smart_ptr<
Class, Other, Policy, Other, std::true_type> {
using type = void; // it doesn't matter which type, it's just for SFNIAE.
};
struct same_smart_ptr_aux<
Class, Other, Policy,
std::void_t<typename virtual_traits<Class, Policy>::template rebind<
typename Other::element_type>>>
: std::is_same<
Other,
typename virtual_traits<Class, Policy>::template rebind<
typename Other::element_type>> {};
template<class Class, class Other, class Policy>
constexpr bool same_smart_ptr = same_smart_ptr_aux<Class, Other, Policy>::value;
template<class Class, class Policy>
class virtual_ptr_impl<
Class, Policy,
std::void_t<
typename virtual_traits<Class, Policy>::template rebind<Class>>> {
public:
using traits = virtual_traits<Class, Policy>;
using element_type = typename Class::element_type;
template<class, class>
friend class virtual_ptr;
template<class, class, typename>
friend class virtual_ptr_impl;
template<class, class>
friend struct virtual_traits;
@@ -409,50 +509,146 @@ class virtual_ptr_impl<
static constexpr bool use_indirect_vptrs =
Policy::template has_facet<policies::indirect_vptr>;
std::conditional_t<use_indirect_vptrs, const vptr_type&, vptr_type> vp;
std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type> vp;
Class obj;
public:
static constexpr bool is_smart_ptr = true;
virtual_ptr_impl() : vp(box_vptr<use_indirect_vptrs>(null_vptr)) {
}
explicit virtual_ptr_impl(std::nullptr_t)
: obj(nullptr), vp(box_vptr<use_indirect_vptrs>(null_vptr)) {
}
virtual_ptr_impl(const virtual_ptr_impl& other) = default;
template<
class Other,
typename =
typename enable_if_compatible_smart_ptr<Class, Other, Policy>::type>
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, const Other&>>>
virtual_ptr_impl(const Other& other)
: obj(other), vp(Policy::dynamic_vptr(*other)) {
: obj(other), vp(box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr)) {
}
template<
class Other,
typename =
typename enable_if_compatible_smart_ptr<Class, Other, Policy>::type>
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, Other&>>>
virtual_ptr_impl(Other& other)
: obj(other), vp(box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr)) {
}
template<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_constructible_v<Class, Other&&>>>
virtual_ptr_impl(Other&& other)
: obj(std::move(other)), vp(Policy::dynamic_vptr(*other)) {
: obj(std::move(other)),
vp(box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr)) {
}
template<
class Other,
typename =
typename enable_if_compatible_smart_ptr<Class, Other, Policy>::type>
virtual_ptr_impl(virtual_ptr<Other, Policy>& other)
: obj(other.obj), vp(other.vp) {
}
template<
class Other,
typename =
typename enable_if_compatible_smart_ptr<Class, Other, Policy>::type>
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)
: obj(other.obj), vp(other.vp) {
}
template<
class Other,
typename =
typename enable_if_compatible_smart_ptr<Class, Other, Policy>::type>
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)
: obj(other.obj), vp(other.vp) {
}
virtual_ptr_impl(virtual_ptr_impl&& other)
: obj(std::move(other.obj)), vp(other.vp) {
other.vp = box_vptr<use_indirect_vptrs>(null_vptr);
}
template<
class Other,
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)
: obj(std::move(other.obj)), vp(other.vp) {
other.vp = box_vptr<use_indirect_vptrs>(null_vptr);
}
virtual_ptr_impl& operator=(std::nullptr_t) {
obj = nullptr;
vp = box_vptr<use_indirect_vptrs>(null_vptr);
return *this;
}
template<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, const Other&>>>
virtual_ptr_impl& operator=(const Other& other) {
obj = other;
vp = box_vptr<use_indirect_vptrs>(Policy::dynamic_vptr(*other));
return *this;
}
template<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, Other&&>>>
virtual_ptr_impl& operator=(Other&& other) {
vp = box_vptr<use_indirect_vptrs>(
other ? Policy::dynamic_vptr(*other) : null_vptr);
obj = std::move(other);
return *this;
}
template<
class Other,
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) {
obj = other.obj;
vp = other.vp;
return *this;
}
template<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, const Other&>>>
virtual_ptr_impl& operator=(const virtual_ptr_impl<Other, Policy>& other) {
obj = other.obj;
vp = other.vp;
return *this;
}
template<
class Other,
typename = std::enable_if_t<
same_smart_ptr<Class, Other, Policy> &&
std::is_assignable_v<Class, Other&&>>>
virtual_ptr_impl& operator=(virtual_ptr_impl<Other, Policy>&& other) {
obj = std::move(other.obj);
vp = other.vp;
other.vp = box_vptr<use_indirect_vptrs>(null_vptr);
return *this;
}
auto get() const -> element_type* {
@@ -472,8 +668,8 @@ class virtual_ptr_impl<
}
template<typename Arg>
virtual_ptr_impl(Arg&& obj, const vptr_type& vp)
: obj(std::forward<Arg>(obj)), vp(vp) {
virtual_ptr_impl(Arg&& obj, decltype(vp) other_vp)
: obj(std::forward<Arg>(obj)), vp(other_vp) {
}
template<class Other>
@@ -526,6 +722,14 @@ class virtual_ptr : public detail::virtual_ptr_impl<Class, Policy> {
template<class, class, typename>
friend class detail::virtual_ptr_impl;
template<
typename Other,
typename = std::enable_if_t<std::is_assignable_v<impl, Other>>>
virtual_ptr& operator=(Other&& other) {
impl::operator=(std::forward<Other>(other));
return *this;
}
template<class Other>
static auto final(Other&& obj) {
using other_traits = virtual_traits<Other, Policy>;
@@ -550,11 +754,12 @@ class virtual_ptr : public detail::virtual_ptr_impl<Class, Policy> {
return virtual_ptr(
std::forward<Other>(obj),
Policy::template static_vptr<other_class>);
detail::box_vptr<impl::use_indirect_vptrs>(
Policy::template static_vptr<other_class>));
}
auto vptr() const {
return this->vp;
return detail::unbox_vptr(this->vp);
}
};

View File

@@ -139,14 +139,12 @@ void fast_perfect_hash<Policy>::hash_initialize(
}
if (found) {
++hash_max;
if constexpr (trace_enabled) {
if (Policy::trace_enabled) {
Policy::trace_stream << " found " << hash_mult << " after "
<< total_attempts
<< " attempts; span = [" << hash_min
<< "," << hash_max << ")\n";
<< ", " << hash_max << "]\n";
}
}
@@ -167,7 +165,7 @@ void fast_perfect_hash<Policy>::hash_initialize(
template<class Policy>
void fast_perfect_hash<Policy>::check(std::size_t index, type_id type) {
if (index < hash_min || index >= hash_max ||
if (index < hash_min || index > hash_max ||
detail::fast_perfect_hash_control<Policy>[index] != type) {
if constexpr (Policy::template has_facet<error_handler>) {
unknown_class_error error;

View File

@@ -37,7 +37,7 @@ class vptr_vector : public extern_vptr,
if constexpr (Policy::template has_facet<type_hash>) {
auto report = Policy::hash_initialize(first, last);
size = report.last;
size = report.last + 1;
} else {
size = 0;

View File

@@ -33,7 +33,9 @@ run test_rolex.cpp ;
run test_static_list.cpp ;
run test_virtual_ptr_all.cpp ;
run test_virtual_ptr_basic.cpp ;
run test_virtual_ptr_state.cpp ;
run test_virtual_ptr_value_semantics.cpp ;
run test_shared_virtual_ptr_value_semantics.cpp ;
run test_unique_virtual_ptr_value_semantics.cpp ;
# quick (for CI)
alias quick : blackbox ;

View File

@@ -130,6 +130,37 @@ BOOST_AUTO_TEST_CASE(cast_args_rvalue_refs) {
}
} // namespace TEST_NS
// -----------------------------------------------------------------------------
// pass virtual args by pointer
namespace TEST_NS {
using policy = test_policy_<__COUNTER__>;
using namespace animals;
BOOST_OPENMETHOD_CLASSES(Animal, Dog, Cat, policy);
BOOST_OPENMETHOD(name, (virtual_<const Animal*>), std::string, policy);
BOOST_OPENMETHOD_OVERRIDE(name, (const Cat* cat), std::string) {
return cat->owner + "'s cat " + cat->name;
}
BOOST_OPENMETHOD_OVERRIDE(name, (const Dog* dog), std::string) {
return dog->owner + "'s dog " + dog->name;
}
BOOST_AUTO_TEST_CASE(cast_args_pointer) {
initialize<policy>();
Dog spot("Spot");
BOOST_TEST(name(&spot) == "Bill's dog Spot");
Cat felix("Felix");
BOOST_TEST(name(&felix) == "Bill's cat Felix");
}
} // namespace TEST_NS
namespace TEST_NS {
// -----------------------------------------------------------------------------

View File

@@ -7,6 +7,7 @@
#include <type_traits>
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include "test_util.hpp"
@@ -17,12 +18,11 @@ using namespace boost::openmethod;
using namespace boost::openmethod::detail;
namespace mp11 = boost::mp11;
// clang-format off
namespace test_virtual {
struct base {
virtual ~base() {}
virtual ~base() {
}
};
struct a : base {};
@@ -33,62 +33,86 @@ struct e : base {};
struct f : base {};
static_assert(
std::is_same_v<
virtual_traits<base&, default_policy>::virtual_type, base>);
std::is_same_v<virtual_traits<base&, default_policy>::virtual_type, base>);
static_assert(std::is_same_v<
virtual_traits<const base&, default_policy>::virtual_type, base>);
static_assert(
std::is_same_v<virtual_traits<base&&, default_policy>::virtual_type, base>);
static_assert(
std::is_same_v<virtual_traits<int, default_policy>::virtual_type, void>);
static_assert(std::is_same_v<
boost::mp11::mp_filter<
is_virtual, mp11::mp_list<virtual_<a&>, b, virtual_<c&>>>,
mp11::mp_list<virtual_<a&>, virtual_<c&>>>);
static_assert(std::is_same_v<remove_virtual<virtual_<a&>>, a&>);
static_assert(std::is_same_v<virtual_type<a&, default_policy>, a>);
static_assert(
std::is_same_v<
virtual_traits<const base&, default_policy>::virtual_type, base>);
virtual_types<mp11::mp_list<
virtual_<std::shared_ptr<a>>, b, virtual_<std::shared_ptr<c>>>>,
mp11::mp_list<std::shared_ptr<a>, std::shared_ptr<c>>>);
static_assert(std::is_same_v<
overrider_virtual_types<
mp11::mp_list<virtual_<a&>, b, virtual_<c&>>,
mp11::mp_list<d&, e, f&>, default_policy>,
mp11::mp_list<d, f>>);
static_assert(
std::is_same_v<virtual_type<std::shared_ptr<a>, default_policy>, a>);
static_assert(std::is_same_v<
virtual_traits<virtual_ptr<a>, default_policy>::virtual_type, a>);
static_assert(std::is_same_v<
select_overrider_virtual_type_aux<
virtual_ptr<base>, virtual_ptr<a>, default_policy>::type,
a>);
static_assert(
std::is_same_v<
virtual_traits<base&&, default_policy>::virtual_type, base>);
overrider_virtual_types<
mp11::mp_list<virtual_ptr<a>, b, virtual_ptr<c>>,
mp11::mp_list<virtual_ptr<d>, e, virtual_ptr<f>>, default_policy>,
mp11::mp_list<d, f>>);
static_assert(
std::is_same_v<
virtual_traits<int, default_policy>::virtual_type, void>);
overrider_virtual_types<
mp11::mp_list<
const virtual_ptr<base>&, b, const virtual_ptr<base>&>,
mp11::mp_list<const virtual_ptr<d>&, e, const virtual_ptr<f>&>,
default_policy>,
mp11::mp_list<d, f>>);
static_assert(
std::is_same_v<
boost::mp11::mp_filter<
is_virtual,
mp11::mp_list< virtual_<a&>, b, virtual_<c&> >
>,
mp11::mp_list< virtual_<a&>, virtual_<c&> >
>);
overrider_virtual_types<
mp11::mp_list<
virtual_<std::shared_ptr<a>>, b, virtual_<std::shared_ptr<c>>>,
mp11::mp_list<std::shared_ptr<d>, e, std::shared_ptr<f>>,
default_policy>,
mp11::mp_list<d, f>>);
static_assert(
std::is_same_v<
remove_virtual<virtual_<a&>>,
a&
>);
static_assert(
std::is_same_v<
virtual_type<a&, default_policy>,
a
>);
static_assert(
std::is_same_v<
boost::mp11::mp_transform<
remove_virtual,
mp11::mp_list< virtual_<a&>, virtual_<c&> >
>,
mp11::mp_list<a&, c&>
>);
static_assert(std::is_same_v<
boost::mp11::mp_transform<
remove_virtual, mp11::mp_list<virtual_<a&>, virtual_<c&>>>,
mp11::mp_list<a&, c&>>);
static_assert(
std::is_same_v<
boost::mp11::mp_transform_q<
boost::mp11::mp_bind_back<virtual_type, default_policy>,
boost::mp11::mp_transform<
remove_virtual,
mp11::mp_list< virtual_<a&>, virtual_<c&> >
>
>,
mp11::mp_list<a, c>
>);
remove_virtual, mp11::mp_list<virtual_<a&>, virtual_<c&>>>>,
mp11::mp_list<a, c>>);
static_assert(
std::is_same_v<
@@ -97,13 +121,8 @@ static_assert(
boost::mp11::mp_transform<
remove_virtual,
boost::mp11::mp_filter<
is_virtual,
mp11::mp_list< virtual_<a&>, b, virtual_<c&> >
>
>
>,
mp11::mp_list<a, c>
>);
is_virtual, mp11::mp_list<virtual_<a&>, b, virtual_<c&>>>>>,
mp11::mp_list<a, c>>);
// clang-format on

View File

@@ -0,0 +1,273 @@
// qright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or q at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod/shared_ptr.hpp>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/included/unit_test.hpp>
#include "test_virtual_ptr_value_semantics.hpp"
#include <memory>
static_assert(detail::same_smart_ptr<
std::shared_ptr<Animal>, std::shared_ptr<Dog>, default_policy>);
static_assert(!detail::same_smart_ptr<
std::shared_ptr<Animal>, std::unique_ptr<Dog>, default_policy>);
static_assert(!detail::same_smart_ptr<
std::shared_ptr<Animal>, shared_virtual_ptr<std::unique_ptr<Dog>>,
default_policy>);
BOOST_AUTO_TEST_CASE_TEMPLATE(shared_virtual_ptr_value, Policy, test_policies) {
static_assert(
std::is_same_v<
typename shared_virtual_ptr<Animal, Policy>::element_type, Animal>);
static_assert(
std::is_same_v<
decltype(std::declval<shared_virtual_ptr<Animal, Policy>>().get()),
Animal*>);
static_assert(shared_virtual_ptr<Animal, Policy>::is_smart_ptr);
static_assert(shared_virtual_ptr<const Animal, Policy>::is_smart_ptr);
static_assert(std::is_same_v<
decltype(*std::declval<shared_virtual_ptr<Animal, Policy>>()),
Animal&>);
init_test<Policy>();
// construction and assignment from a plain pointer or reference is not
// allowed
static_assert(
!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, Dog>);
static_assert(
!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, Dog&&>);
static_assert(
!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, const Dog&>);
static_assert(
!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, const Dog*>);
// -------------------------------------------------------------------------
// construction and assignment from plain references and pointers
{
shared_virtual_ptr<Dog, Policy> p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
auto snoopy = std::make_shared<Dog>();
shared_virtual_ptr<Dog, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto hector = std::make_shared<Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
auto snoopy = std::make_shared<Dog>();
shared_virtual_ptr<Animal, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto felix = std::make_shared<Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
{
auto snoopy = std::make_shared<const Dog>();
shared_virtual_ptr<const Dog, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto hector = std::make_shared<const Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
auto snoopy = std::make_shared<const Dog>();
shared_virtual_ptr<const Animal, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto felix = std::make_shared<const Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
{
auto snoopy = std::make_shared<Dog>();
shared_virtual_ptr<Dog, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto hector = std::make_shared<Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
auto snoopy = std::make_shared<Dog>();
shared_virtual_ptr<Animal, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto felix = std::make_shared<Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
{
auto snoopy = std::make_shared<const Dog>();
shared_virtual_ptr<const Dog, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto hector = std::make_shared<const Dog>();
p = hector;
BOOST_TEST(p.get() == hector.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
auto snoopy = std::make_shared<const Dog>();
shared_virtual_ptr<const Animal, Policy> p(snoopy);
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
auto felix = std::make_shared<const Cat>();
p = felix;
BOOST_TEST(p.get() == felix.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
// shared_virtual_ptr<Dog, Policy> p{Dog()};
static_assert(!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, Dog&&>);
// -------------------------------------------------------------------------
// construction and assignment from other shared_virtual_ptr
{
// shared_virtual_ptr<Dog>(const shared_virtual_ptr<Dog>&)
auto snoopy = std::make_shared<Dog>();
const shared_virtual_ptr<Dog, Policy> p(snoopy);
shared_virtual_ptr<Dog, Policy> q(p);
BOOST_TEST(q.get() == snoopy.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<Dog>(shared_virtual_ptr<Dog>&)
auto snoopy = std::make_shared<Dog>();
shared_virtual_ptr<Dog, Policy> p(snoopy);
shared_virtual_ptr<Dog, Policy> q(p);
BOOST_TEST(q.get() == snoopy.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<Dog>(shared_virtual_ptr<Dog>&&)
auto snoopy = std::make_shared<Dog>();
shared_virtual_ptr<Dog, Policy> p(snoopy);
shared_virtual_ptr<Dog, Policy> q(std::move(p));
BOOST_TEST(q.get() == snoopy.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
// shared_virtual_ptr<Animal>(const shared_virtual_ptr<Dog>&)
auto snoopy = std::make_shared<Dog>();
const shared_virtual_ptr<Dog, Policy> p(snoopy);
shared_virtual_ptr<Animal, Policy> base(p);
BOOST_TEST(base.get() == snoopy.get());
BOOST_TEST(base.vptr() == Policy::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<const Dog>(const shared_virtual_ptr<Dog>&)
auto snoopy = std::make_shared<Dog>();
const shared_virtual_ptr<Dog, Policy> p(snoopy);
shared_virtual_ptr<const Dog, Policy> const_q(p);
BOOST_TEST(const_q.get() == snoopy.get());
BOOST_TEST(const_q.vptr() == Policy::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<const Animal>(const shared_virtual_ptr<Dog>&)
auto snoopy = std::make_shared<Dog>();
const shared_virtual_ptr<Dog, Policy> p(snoopy);
shared_virtual_ptr<const Animal, Policy> const_base_q(p);
BOOST_TEST(const_base_q.get() == snoopy.get());
BOOST_TEST(const_base_q.vptr() == Policy::template static_vptr<Dog>);
}
{
// shared_virtual_ptr<Dog>()
shared_virtual_ptr<Dog, Policy> p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
shared_virtual_ptr<Dog, Policy> p{std::shared_ptr<Dog>()};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
// -------------------------------------------------------------------------
// assignment
{
shared_virtual_ptr<Dog, Policy> p;
auto snoopy = std::make_shared<Dog>();
p = snoopy;
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
shared_virtual_ptr<Dog, Policy> p;
auto snoopy = std::make_shared<Dog>();
p = snoopy;
BOOST_TEST(p.get() == snoopy.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
auto p = make_shared_virtual<Dog>();
p = nullptr;
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
auto p = make_shared_virtual<Dog>();
p = std::shared_ptr<Dog>();
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
static_assert(
!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, const Dog&>);
static_assert(
!construct_assign_ok<shared_virtual_ptr<Dog, Policy>, const Dog*>);
}
template struct check_illegal_smart_ops<
std::shared_ptr, std::unique_ptr, direct_vector_policy>;

View File

@@ -0,0 +1,256 @@
// 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 <boost/openmethod/unique_ptr.hpp>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/included/unit_test.hpp>
#include "test_virtual_ptr_value_semantics.hpp"
#include <memory>
BOOST_AUTO_TEST_CASE_TEMPLATE(unique_virtual_ptr_value, Policy, test_policies) {
init_test<Policy>();
static_assert(
std::is_same_v<
typename unique_virtual_ptr<Animal, Policy>::element_type, Animal>);
static_assert(
std::is_same_v<
decltype(std::declval<unique_virtual_ptr<Animal, Policy>>().get()),
Animal*>);
static_assert(unique_virtual_ptr<Animal, Policy>::is_smart_ptr);
static_assert(unique_virtual_ptr<const Animal, Policy>::is_smart_ptr);
static_assert(std::is_same_v<
decltype(*std::declval<unique_virtual_ptr<Animal, Policy>>()),
Animal&>);
{
// unique_virtual_ptr<Dog>(nullptr)
unique_virtual_ptr<Dog, Policy> p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
static_assert(!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, Dog>);
static_assert(!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, Dog&>);
static_assert(!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, Dog*>);
static_assert(!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, std::unique_ptr<Dog>&>);
static_assert(
!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, const std::unique_ptr<Dog>&>);
static_assert(!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, unique_virtual_ptr<Dog>>);
static_assert(!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, unique_virtual_ptr<Dog>&>);
static_assert(
!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, const unique_virtual_ptr<Dog>&>);
{
// construct from unique_ptr temporary
unique_virtual_ptr<Dog, Policy> p(std::make_unique<Dog>());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
// derived-to-base ok?
unique_virtual_ptr<Animal, Policy> p(std::make_unique<Dog>());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
static_assert(
!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, const std::unique_ptr<Dog>&>);
static_assert(!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, std::unique_ptr<Dog>&>);
static_assert(!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>,
const unique_virtual_ptr<Dog, Policy>&>);
static_assert(
!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, unique_virtual_ptr<Dog, Policy>&>);
static_assert(
!construct_assign_ok<
unique_virtual_ptr<Dog, Policy>, unique_virtual_ptr<Dog, Policy>&>);
{
// assign from smart ptr temporary
unique_virtual_ptr<Dog, Policy> p{nullptr};
p = std::make_unique<Dog>();
BOOST_TEST(p.get() != nullptr);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
// derived-to-base ok?
unique_virtual_ptr<Animal, Policy> p{nullptr};
p = std::make_unique<Dog>();
BOOST_TEST(p.get() != nullptr);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
// unique_virtual_ptr<Dog>(unique_virtual_ptr<Dog>)
unique_virtual_ptr<Dog, Policy> p(std::make_unique<Dog>());
auto dog = p.get();
unique_virtual_ptr<Dog, Policy> q(std::move(p));
BOOST_TEST(q.get() == dog);
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
// derived-to-base ok?
unique_virtual_ptr<Dog, Policy> p(std::make_unique<Dog>());
auto dog = p.get();
unique_virtual_ptr<Animal, Policy> q(std::move(p));
BOOST_TEST(q.get() == dog);
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
// virtual_ptr<Dog>(std::unique_ptr<Dog>())
unique_virtual_ptr<Dog, Policy> p = std::unique_ptr<Dog>();
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
unique_virtual_ptr<Dog, Policy> p(std::make_unique<Dog>());
p = nullptr;
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
unique_virtual_ptr<Dog, Policy> p(std::make_unique<Dog>());
p = std::unique_ptr<Dog>();
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
#if 0
{
// unique_virtual_ptr<Dog> = const std::unique_ptr<Dog>&
unique_virtual_ptr<Dog, Policy> p;
const auto s = std::make_unique<Dog>();
p = s;
BOOST_TEST(p.get() == s.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
// unique_virtual_ptr<Dog> = std::unique_ptr<Dog>&
unique_virtual_ptr<Dog, Policy> p;
auto s = std::make_unique<Dog>();
p = s;
BOOST_TEST(p.get() == s.get());
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
// unique_virtual_ptr<Dog> = std::unique_ptr<Dog>&&
auto s = std::make_unique<Dog>();
auto p = s;
unique_virtual_ptr<Dog, Policy> q;
q = std::move(p);
BOOST_TEST(q.get() == s.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
}
{
// unique_virtual_ptr<Animal> = std::unique_ptr<Dog>&&
auto s = std::make_unique<Dog>();
auto p = s;
unique_virtual_ptr<Animal, Policy> q;
q = std::move(p);
BOOST_TEST(q.get() == s.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
}
{
// unique_virtual_ptr<Dog> = unique_virtual_ptr<Dog>&&
auto s = std::make_unique<Dog>();
unique_virtual_ptr<Dog, Policy> p(s);
unique_virtual_ptr<Dog, Policy> q;
q = std::move(p);
BOOST_TEST(q.get() == s.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
// unique_virtual_ptr<Animal> = unique_virtual_ptr<Dog>&&
auto s = std::make_unique<Dog>();
unique_virtual_ptr<Dog, Policy> p(s);
unique_virtual_ptr<Animal, Policy> q;
q = std::move(p);
BOOST_TEST(q.get() == s.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
// virtual_ptr<Dog>(unique_virtual_ptr<Dog>&)
auto p = make_unique_virtual<Dog, Policy>();
virtual_ptr<Dog, Policy> q(p);
BOOST_TEST(q.get() == p.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<Dog> = unique_virtual_ptr<Dog>&
const auto p = make_unique_virtual<Dog, Policy>();
virtual_ptr<Dog, Policy> q;
q = p;
BOOST_TEST(q.get() == p.get());
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
}
// illegal constructions and assignments
static_assert(
!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, Dog>);
static_assert(
!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, Dog&&>);
static_assert(
!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, const Dog&>);
static_assert(
!construct_assign_ok<unique_virtual_ptr<Dog, Policy>, const Dog*>);
static_assert(!std::is_assignable_v<unique_virtual_ptr<Dog, Policy>, Dog>);
static_assert(
!std::is_assignable_v<unique_virtual_ptr<Dog, Policy>, Dog&&>);
static_assert(
!std::is_assignable_v<unique_virtual_ptr<Dog, Policy>, const Dog&>);
static_assert(
!std::is_assignable_v<unique_virtual_ptr<Dog, Policy>, const Dog*>);
#endif
}
template struct check_illegal_smart_ops<
std::unique_ptr, std::shared_ptr, direct_vector_policy>;

View File

@@ -1,227 +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)
#include <boost/openmethod.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/compiler.hpp>
#include <iostream>
#include <memory>
#include <string>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/included/unit_test.hpp>
#include <boost/test/tools/output_test_stream.hpp>
#include "test_util.hpp"
using boost::mp11::mp_list;
using std::cout;
using namespace boost::openmethod;
using namespace boost::openmethod::detail;
struct base {
virtual ~base() {
}
};
struct a : base {};
struct b : base {};
struct c : base {};
struct d : base {};
struct e : base {};
struct f : base {};
static_assert(
std::is_same_v<
virtual_types<mp_list<
virtual_<std::shared_ptr<a>>, b, virtual_<std::shared_ptr<c>>>>,
mp_list<std::shared_ptr<a>, std::shared_ptr<c>>>);
static_assert(std::is_same_v<
overrider_virtual_types<
mp_list<virtual_<a&>, b, virtual_<c&>>, mp_list<d&, e, f&>,
default_policy>,
mp_list<d, f>>);
static_assert(
std::is_same_v<virtual_type<std::shared_ptr<a>, default_policy>, a>);
static_assert(std::is_same_v<
virtual_traits<virtual_ptr<a>, default_policy>::virtual_type, a>);
static_assert(std::is_same_v<
select_overrider_virtual_type_aux<
virtual_ptr<base>, virtual_ptr<a>, default_policy>::type,
a>);
static_assert(std::is_same_v<
overrider_virtual_types<
mp_list<virtual_ptr<a>, b, virtual_ptr<c>>,
mp_list<virtual_ptr<d>, e, virtual_ptr<f>>, default_policy>,
mp_list<d, f>>);
static_assert(
std::is_same_v<
overrider_virtual_types<
mp_list<const virtual_ptr<base>&, b, const virtual_ptr<base>&>,
mp_list<const virtual_ptr<d>&, e, const virtual_ptr<f>&>,
default_policy>,
mp_list<d, f>>);
static_assert(
std::is_same_v<
overrider_virtual_types<
mp_list<
virtual_<std::shared_ptr<a>>, b, virtual_<std::shared_ptr<c>>>,
mp_list<std::shared_ptr<d>, e, std::shared_ptr<f>>, default_policy>,
mp_list<d, f>>);
namespace using_polymorphic_classes {
struct Animal {
virtual ~Animal() {
}
};
struct Dog : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog);
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(poke, (const virtual_ptr<Animal>&, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(
poke, (const virtual_ptr<Dog>&, std::ostream& os), void) {
os << "bark";
}
static_assert(sizeof(virtual_ptr<Animal>) == 2 * sizeof(void*));
BOOST_AUTO_TEST_CASE(test_virtual_ptr_by_ref) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
Dog dog;
virtual_ptr<Animal> vptr(dog);
poke(vptr, os);
}
{
// Using deduction guide.
boost::test_tools::output_test_stream os;
Animal&& animal = Dog();
auto vptr = virtual_ptr(animal);
poke(vptr, os);
BOOST_CHECK(os.is_equal("bark"));
}
{
// Using conversion ctor.
boost::test_tools::output_test_stream os;
Animal&& animal = Dog();
poke(animal, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_shared_by_value) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
poke(animal, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(
poke, (const shared_virtual_ptr<Animal>&, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(
poke, (const shared_virtual_ptr<Dog>&, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_shared_by_const_reference) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
poke(animal, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(poke, (unique_virtual_ptr<Animal>, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(
poke, (unique_virtual_ptr<Dog>, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_unique) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
poke(make_unique_virtual<Dog>(), os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
} // namespace using_polymorphic_classes
namespace using_non_polymorphic_classes {
struct Animal {};
struct Dog : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog);
BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_ptr_non_polymorphic) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
Dog dog;
auto vptr = virtual_ptr<Dog>::final(dog);
poke(vptr, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace using_non_polymorphic_classes

View File

@@ -4,16 +4,171 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/openmethod.hpp>
#include <boost/openmethod/compiler.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/compiler.hpp>
#include "test_util.hpp"
#include <iostream>
#include <memory>
#include <string>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/included/unit_test.hpp>
#include <boost/utility/identity_type.hpp>
#include <boost/test/tools/output_test_stream.hpp>
#include "test_util.hpp"
using boost::mp11::mp_list;
using std::cout;
using namespace boost::openmethod;
using namespace boost::openmethod::detail;
namespace using_polymorphic_classes {
struct Animal {
virtual ~Animal() {
}
};
struct Dog : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog);
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(poke, (const virtual_ptr<Animal>&, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(
poke, (const virtual_ptr<Dog>&, std::ostream& os), void) {
os << "bark";
}
static_assert(sizeof(virtual_ptr<Animal>) == 2 * sizeof(void*));
BOOST_AUTO_TEST_CASE(test_virtual_ptr_by_ref) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
Dog dog;
virtual_ptr<Animal> vptr(dog);
poke(vptr, os);
}
{
// Using deduction guide.
boost::test_tools::output_test_stream os;
Animal&& animal = Dog();
auto vptr = virtual_ptr(animal);
poke(vptr, os);
BOOST_CHECK(os.is_equal("bark"));
}
{
// Using conversion ctor.
boost::test_tools::output_test_stream os;
Animal&& animal = Dog();
poke(animal, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_shared_by_value) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
poke(animal, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(
poke, (const shared_virtual_ptr<Animal>&, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(
poke, (const shared_virtual_ptr<Dog>&, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_shared_by_const_reference) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
poke(animal, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
namespace BOOST_OPENMETHOD_GENSYM {
BOOST_OPENMETHOD(poke, (unique_virtual_ptr<Animal>, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(
poke, (unique_virtual_ptr<Dog>, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_unique) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
poke(make_unique_virtual<Dog>(), os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace BOOST_OPENMETHOD_GENSYM
} // namespace using_polymorphic_classes
namespace using_non_polymorphic_classes {
struct Animal {};
struct Dog : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Dog);
BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>, std::ostream&), void);
BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>, std::ostream& os), void) {
os << "bark";
}
BOOST_AUTO_TEST_CASE(test_virtual_ptr_non_polymorphic) {
boost::openmethod::initialize();
{
boost::test_tools::output_test_stream os;
Dog dog;
auto vptr = virtual_ptr<Dog>::final(dog);
poke(vptr, os);
BOOST_CHECK(os.is_equal("bark"));
}
}
} // namespace using_non_polymorphic_classes
struct Player {
virtual ~Player() {

View File

@@ -1,403 +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)
#include <iostream>
#include <memory>
#include <string>
#define BOOST_TEST_MODULE openmethod
#include <boost/test/included/unit_test.hpp>
#include <boost/core/demangle.hpp>
#include <boost/utility/identity_type.hpp>
#include <boost/openmethod.hpp>
#include <boost/openmethod/policies/vptr_map.hpp>
#include <boost/openmethod/compiler.hpp>
#include <boost/openmethod/shared_ptr.hpp>
#include <boost/openmethod/unique_ptr.hpp>
using namespace boost::openmethod;
using namespace boost::openmethod::policies;
struct Animal {
virtual ~Animal() {
}
Animal() = default;
Animal(const Animal&) = delete;
int age;
};
struct Cat : virtual Animal {};
struct Dog : virtual Animal {};
template<class Policy>
void init_test() {
BOOST_OPENMETHOD_REGISTER(use_classes<Animal, Dog, Policy>);
struct id;
(void)&method<id(virtual_ptr<Animal, Policy>), void, Policy>::fn;
boost::openmethod::initialize<Policy>();
}
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>> {};
struct direct_map_policy : default_policy::fork<direct_map_policy>::replace<
extern_vptr, 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>> {};
using test_policies = boost::mp11::mp_list<
direct_vector_policy, indirect_vector_policy, direct_map_policy,
indirect_map_policy>;
BOOST_AUTO_TEST_CASE_TEMPLATE(virtual_ptr_ctors, Policy, test_policies) {
static_assert(std::is_same_v<
typename virtual_ptr<Animal, Policy>::element_type, Animal>);
static_assert(std::is_same_v<
decltype(std::declval<virtual_ptr<Animal, Policy>>().get()),
Animal*>);
static_assert(!virtual_ptr<Animal, Policy>::is_smart_ptr);
static_assert(!virtual_ptr<const Animal, Policy>::is_smart_ptr);
static_assert(
std::is_same_v<
decltype(*std::declval<virtual_ptr<Animal, Policy>>()), Animal&>);
static_assert(!std::is_constructible_v<virtual_ptr<Animal, Policy>, Dog>);
init_test<Policy>();
{
Dog dog;
{
virtual_ptr<Dog, Policy> p(dog);
BOOST_TEST(p.vptr() != nullptr);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
virtual_ptr<Dog, Policy> copy(p);
virtual_ptr<Animal, Policy> base(p);
virtual_ptr<const Dog, Policy> const_copy(p);
virtual_ptr<const Animal, Policy> const_base_copy(p);
}
{
const auto p = virtual_ptr<Dog, Policy>(dog);
virtual_ptr<Dog, Policy> const_copy(p);
virtual_ptr<Animal, Policy> const_base_copy(p);
}
{
auto p = virtual_ptr<const Animal, Policy>(dog);
}
}
}
template<
template<class... Class> class smart_ptr,
template<class... Class> class other_smart_ptr, class Policy>
struct check_smart_ctors {
// construction
// a virtual_ptr can be constructed from a smart_ptr (same class)
static_assert(std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, smart_ptr<Animal>>);
// a virtual_ptr to const can be constructed from smart_ptr (same class)
static_assert(
std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, const smart_ptr<Animal>&>);
static_assert(
std::is_constructible_v<
virtual_ptr<smart_ptr<const Animal>, Policy>, smart_ptr<Animal>>);
// a virtual_ptr can be constructed from a smart_ptr (derived class)
static_assert(std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, smart_ptr<Dog>>);
// a virtual_ptr to const can be constructed from a smart_ptr (derived class)
static_assert(
std::is_constructible_v<
virtual_ptr<smart_ptr<const Animal>, Policy>, smart_ptr<Dog>>);
// a virtual_ptr cannot be constructed from a smart_ptr to a different class
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Cat>, Policy>, smart_ptr<Dog>>);
// a virtual_ptr cannot be constructed from const smart_ptr
static_assert(
!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, smart_ptr<const Animal>>);
// policies must be the same
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, policies::debug>,
virtual_ptr<smart_ptr<Animal>, policies::release>>);
// move constructible
static_assert(
std::is_move_constructible_v<virtual_ptr<smart_ptr<Animal>, Policy>>);
// a smart virtual_ptr cannot be constructed from a plain reference or
// pointer
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, Animal>);
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, Animal*>);
// the smart pointer must be the same (e.g. both shared_ptr)
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>,
virtual_ptr<other_smart_ptr<Animal>, Policy>>);
// a smart virtual_ptr converts to a plain one
static_assert(std::is_constructible_v<
virtual_ptr<Animal, Policy>,
virtual_ptr<smart_ptr<Animal>, Policy>>);
// but not the other way around
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>,
virtual_ptr<Animal, Policy>>);
// ---------------------
// test other properties
static_assert(virtual_ptr<smart_ptr<Animal>, Policy>::is_smart_ptr);
static_assert(virtual_ptr<smart_ptr<const Animal>, Policy>::is_smart_ptr);
static_assert(std::is_same_v<
typename virtual_ptr<smart_ptr<Animal>, Policy>::element_type,
Animal>);
static_assert(
std::is_same_v<
decltype(std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()
.get()),
Animal*>);
static_assert(
std::is_same_v<
decltype(*std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()),
Animal&>);
static_assert(
std::is_same_v<
decltype(std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()
.pointer()),
const smart_ptr<Animal>&>);
static_assert(
std::is_same_v<
decltype(*std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()),
Animal&>);
};
template struct check_smart_ctors<
std::shared_ptr, std::unique_ptr, direct_vector_policy>;
template struct check_smart_ctors<
std::unique_ptr, std::shared_ptr, direct_vector_policy>;
BOOST_AUTO_TEST_CASE_TEMPLATE(shared_virtual_ptr_ctors, Policy, test_policies) {
{
init_test<Policy>();
auto dog = std::make_shared<Dog>();
{
shared_virtual_ptr<Dog, Policy> ptr(dog);
BOOST_TEST((ptr.get() == dog.get()));
BOOST_TEST(ptr.pointer() == dog);
BOOST_TEST(ptr.vptr() == Policy::template static_vptr<Dog>);
shared_virtual_ptr<Dog, Policy> copy(ptr);
BOOST_TEST((copy.get() == dog.get()));
BOOST_TEST(copy.pointer() == dog);
BOOST_TEST(copy.vptr() == Policy::template static_vptr<Dog>);
shared_virtual_ptr<Animal, Policy> base(ptr);
BOOST_TEST((base.get() == dog.get()));
shared_virtual_ptr<Dog, Policy> downcast =
base.template cast<Dog>();
BOOST_TEST((downcast.get() == dog.get()));
BOOST_TEST(base.vptr() == Policy::template static_vptr<Dog>);
shared_virtual_ptr<const Dog, Policy> const_copy(ptr);
shared_virtual_ptr<const Animal, Policy> base_const_copy(ptr);
shared_virtual_ptr<Animal, Policy> move_ptr(std::move(ptr));
BOOST_TEST(ptr.pointer().get() == nullptr);
BOOST_TEST(move_ptr.pointer().get() == dog.get());
}
{
shared_virtual_ptr<Dog, Policy> ptr(std::make_shared<Dog>());
virtual_ptr<Dog, Policy> dumb_vptr(ptr);
BOOST_TEST(dumb_vptr.get() == ptr.get());
BOOST_TEST(dumb_vptr.vptr() == ptr.vptr());
}
{
shared_virtual_ptr<Dog, Policy> ptr(std::make_shared<Dog>());
virtual_ptr<Dog, Policy> dumb_vptr(ptr);
BOOST_TEST(dumb_vptr.get() == ptr.get());
BOOST_TEST(dumb_vptr.vptr() == ptr.vptr());
}
{
shared_virtual_ptr<Dog, Policy> ptr(dog);
shared_virtual_ptr<const Animal, Policy> move_const_ptr(
std::move(ptr));
BOOST_TEST(ptr.pointer().get() == nullptr);
BOOST_TEST(move_const_ptr.pointer().get() == dog.get());
}
{
shared_virtual_ptr<Animal, Policy> ptr(dog);
}
{
// should not compile:
// unique_virtual_ptr<Dog, Policy> unique_dog(dog);
}
}
{
auto dog = std::make_shared<const Dog>();
shared_virtual_ptr<const Dog, Policy> ptr(dog);
BOOST_TEST(ptr.pointer().get() == dog.get());
BOOST_TEST(ptr.vptr() == Policy::template static_vptr<Dog>);
shared_virtual_ptr<const Dog, Policy> copy(ptr);
shared_virtual_ptr<const Animal, Policy> base(ptr);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(unique_virtual_ptr_ctors, Policy, test_policies) {
init_test<Policy>();
{
// a unique_virtual_ptr can be created from a std::unique_ptr
std::unique_ptr<Dog> is_smart_ptr = std::make_unique<Dog>();
auto dumb_ptr = is_smart_ptr.get();
unique_virtual_ptr<Dog, Policy> virtual_smart_ptr(
std::move(is_smart_ptr));
// and ownership is transferred
BOOST_TEST(is_smart_ptr.get() == nullptr);
BOOST_TEST(virtual_smart_ptr.get() == dumb_ptr);
}
{
// a virtual_ptr can be constructed from a unique_virtual_ptr
auto virtual_smart_ptr = make_unique_virtual<Dog, Policy>();
auto dumb_ptr = virtual_smart_ptr.get();
virtual_ptr<Dog, Policy> virtual_dumb_ptr = virtual_smart_ptr;
BOOST_TEST(virtual_dumb_ptr.get() == dumb_ptr);
// ownership is not transferred
BOOST_TEST(virtual_smart_ptr.get() == dumb_ptr);
}
{
// unique_virtual_ptr can be moved
auto unique = make_unique_virtual<Dog, Policy>();
Dog* dumb_ptr = unique.get();
unique_virtual_ptr<Dog, Policy> unique_moved = std::move(unique);
BOOST_TEST(unique.get() == nullptr);
BOOST_TEST(unique_moved.get() == dumb_ptr);
}
{
unique_virtual_ptr<Animal, Policy> base(std::make_unique<Dog>());
auto p = base.get();
unique_virtual_ptr<Dog, Policy> downcast =
std::move(base).template cast<Dog>();
BOOST_TEST((downcast.get() == p));
BOOST_TEST((base.get() == nullptr));
BOOST_TEST(downcast.vptr() == Policy::template static_vptr<Dog>);
}
}
BOOST_AUTO_TEST_CASE_TEMPLATE(indirect_virtual_ptr, Policy, test_policies) {
BOOST_TEST_MESSAGE(
"Policy = " << boost::core::demangle(typeid(Policy).name()));
init_test<Policy>();
Dog dog;
virtual_ptr<Dog, Policy> p(dog);
BOOST_TEST_MESSAGE("After first call to initialize:");
BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr());
BOOST_TEST_MESSAGE(
"static_vptr<Dog> = " << Policy::template static_vptr<Dog>);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
// Add a class, to make sure dispatch data is not re-constructed in the same
// place with the same values:
struct Cat : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Cat, Policy);
init_test<Policy>();
BOOST_TEST_MESSAGE("After second call to initialize:");
BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr());
BOOST_TEST_MESSAGE(
"static_vptr<Dog> = " << Policy::template static_vptr<Dog>);
if constexpr (Policy::template has_facet<indirect_vptr>) {
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
} else {
BOOST_TEST(p.vptr() != Policy::template static_vptr<Dog>);
}
}
BOOST_AUTO_TEST_CASE(virtual_ptr_final_error) {
auto prev_handler = default_policy::set_error_handler(
[](const default_policy::error_variant& ev) {
if (auto error = std::get_if<type_mismatch_error>(&ev)) {
static_assert(std::is_same_v<
decltype(error), const type_mismatch_error*>);
throw *error;
}
});
init_test<default_policy>();
bool threw = false;
try {
Dog snoopy;
Animal& animal = snoopy;
virtual_ptr<Animal>::final(animal);
} catch (const type_mismatch_error& error) {
default_policy::set_error_handler(prev_handler);
BOOST_TEST(error.type == reinterpret_cast<type_id>(&typeid(Dog)));
threw = true;
} catch (...) {
default_policy::set_error_handler(prev_handler);
BOOST_FAIL("wrong exception");
return;
}
if constexpr (default_policy::has_facet<runtime_checks>) {
if (!threw) {
BOOST_FAIL("should have thrown");
}
} else {
if (threw) {
BOOST_FAIL("should not have thrown");
}
}
}

View File

@@ -0,0 +1,318 @@
// qright (c) 2018-2025 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or q at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_TEST_MODULE openmethod
#include <boost/test/included/unit_test.hpp>
#include "test_virtual_ptr_value_semantics.hpp"
BOOST_AUTO_TEST_CASE_TEMPLATE(plain_virtual_ptr_value, Policy, test_policies) {
static_assert(std::is_same_v<
typename virtual_ptr<Animal, Policy>::element_type, Animal>);
static_assert(std::is_same_v<
decltype(std::declval<virtual_ptr<Animal, Policy>>().get()),
Animal*>);
static_assert(!virtual_ptr<Animal, Policy>::is_smart_ptr);
static_assert(!virtual_ptr<const Animal, Policy>::is_smart_ptr);
static_assert(
std::is_same_v<
decltype(*std::declval<virtual_ptr<Animal, Policy>>()), Animal&>);
init_test<Policy>();
// -------------------------------------------------------------------------
// construction and assignment from plain references and pointers
{
virtual_ptr<Dog, Policy> p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
{
Dog snoopy;
virtual_ptr<Dog, Policy> p(snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
Dog hector;
p = hector;
BOOST_TEST(p.get() == &hector);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
Dog snoopy;
virtual_ptr<Animal, Policy> p(snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
Cat felix;
p = felix;
BOOST_TEST(p.get() == &felix);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
{
const Dog snoopy;
virtual_ptr<const Dog, Policy> p(snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
const Dog hector;
p = hector;
BOOST_TEST(p.get() == &hector);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
const Dog snoopy;
virtual_ptr<const Animal, Policy> p(snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
const Cat felix;
p = felix;
BOOST_TEST(p.get() == &felix);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
{
Dog snoopy;
virtual_ptr<Dog, Policy> p(&snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
Dog hector;
p = &hector;
BOOST_TEST(p.get() == &hector);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
Dog snoopy;
virtual_ptr<Animal, Policy> p(&snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
Cat felix;
p = &felix;
BOOST_TEST(p.get() == &felix);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
{
const Dog snoopy;
virtual_ptr<const Dog, Policy> p(&snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
const Dog hector;
p = &hector;
BOOST_TEST(p.get() == &hector);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
const Dog snoopy;
virtual_ptr<const Animal, Policy> p(&snoopy);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
const Cat felix;
p = &felix;
BOOST_TEST(p.get() == &felix);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Cat>);
}
// virtual_ptr<Dog, Policy> p{Dog()};
static_assert(!construct_assign_ok<virtual_ptr<Dog, Policy>, Dog&&>);
// -------------------------------------------------------------------------
// construction and assignment from other virtual_ptr
{
// virtual_ptr<Dog>(const virtual_ptr<Dog>&)
Dog snoopy;
const virtual_ptr<Dog, Policy> p(snoopy);
virtual_ptr<Dog, Policy> q(p);
BOOST_TEST(q.get() == &snoopy);
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<Dog>(virtual_ptr<Dog>&)
Dog snoopy;
virtual_ptr<Dog, Policy> p(snoopy);
virtual_ptr<Dog, Policy> q(p);
BOOST_TEST(q.get() == &snoopy);
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<Dog>(virtual_ptr<Dog>&&)
Dog snoopy;
virtual_ptr<Dog, Policy> p(snoopy);
virtual_ptr<Dog, Policy> q(std::move(p));
BOOST_TEST(q.get() == &snoopy);
BOOST_TEST(q.vptr() == Policy::template static_vptr<Dog>);
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<Animal>(const virtual_ptr<Dog>&)
Dog snoopy;
const virtual_ptr<Dog, Policy> p(snoopy);
virtual_ptr<Animal, Policy> base(p);
BOOST_TEST(base.get() == &snoopy);
BOOST_TEST(base.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<const Dog>(const virtual_ptr<Dog>&)
Dog snoopy;
const virtual_ptr<Dog, Policy> p(snoopy);
virtual_ptr<const Dog, Policy> const_q(p);
BOOST_TEST(const_q.get() == &snoopy);
BOOST_TEST(const_q.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<const Animal>(const virtual_ptr<Dog>&)
Dog snoopy;
const virtual_ptr<Dog, Policy> p(snoopy);
virtual_ptr<const Animal, Policy> const_base_q(p);
BOOST_TEST(const_base_q.get() == &snoopy);
BOOST_TEST(const_base_q.vptr() == Policy::template static_vptr<Dog>);
}
{
// virtual_ptr<Dog>()
virtual_ptr<Dog, Policy> p{nullptr};
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
// -------------------------------------------------------------------------
// assignment
{
virtual_ptr<Dog, Policy> p;
Dog snoopy;
p = snoopy;
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
virtual_ptr<Dog, Policy> p;
Dog snoopy;
p = &snoopy;
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
virtual_ptr<Animal, Policy> p;
Dog snoopy;
p = snoopy;
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
virtual_ptr<Animal, Policy> p;
Dog snoopy;
p = &snoopy;
BOOST_TEST(p.get() == &snoopy);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
}
{
Dog snoopy;
virtual_ptr<Animal, Policy> p(snoopy);
p = nullptr;
BOOST_TEST(p.get() == nullptr);
BOOST_TEST(p.vptr() == nullptr);
}
static_assert(
!construct_assign_ok<virtual_ptr<Dog, Policy>, const Dog&>);
static_assert(
!construct_assign_ok<virtual_ptr<Dog, Policy>, const Dog*>);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(indirect_virtual_ptr, Policy, test_policies) {
BOOST_TEST_MESSAGE(
"Policy = " << boost::core::demangle(typeid(Policy).name()));
init_test<Policy>();
Dog snoopy;
virtual_ptr<Dog, Policy> p(snoopy);
BOOST_TEST_MESSAGE("After first call to initialize:");
BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr());
BOOST_TEST_MESSAGE(
"static_vptr<Dog> = " << Policy::template static_vptr<Dog>);
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
// Add a class, to make sure dispatch data is not re-constructed in the same
// place with the same values:
struct Cat : Animal {};
BOOST_OPENMETHOD_CLASSES(Animal, Cat, Policy);
init_test<Policy>();
BOOST_TEST_MESSAGE("After second call to initialize:");
BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr());
BOOST_TEST_MESSAGE(
"static_vptr<Dog> = " << Policy::template static_vptr<Dog>);
if constexpr (Policy::template has_facet<indirect_vptr>) {
BOOST_TEST(p.vptr() == Policy::template static_vptr<Dog>);
} else {
BOOST_TEST(p.vptr() != Policy::template static_vptr<Dog>);
}
}
BOOST_AUTO_TEST_CASE(virtual_ptr_final_error) {
auto prev_handler = default_policy::set_error_handler(
[](const default_policy::error_variant& ev) {
if (auto error = std::get_if<type_mismatch_error>(&ev)) {
static_assert(std::is_same_v<
decltype(error), const type_mismatch_error*>);
throw *error;
}
});
init_test<default_policy>();
bool threw = false;
try {
Dog snoopy;
Animal& animal = snoopy;
virtual_ptr<Animal>::final(animal);
} catch (const type_mismatch_error& error) {
default_policy::set_error_handler(prev_handler);
BOOST_TEST(error.type == reinterpret_cast<type_id>(&typeid(Dog)));
threw = true;
} catch (...) {
default_policy::set_error_handler(prev_handler);
BOOST_FAIL("wrong exception");
return;
}
if constexpr (default_policy::has_facet<runtime_checks>) {
if (!threw) {
BOOST_FAIL("should have thrown");
}
} else {
if (threw) {
BOOST_FAIL("should not have thrown");
}
}
}

View File

@@ -0,0 +1,125 @@
// 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 TEST_VIRTUAL_PTR_VALUE_SEMANTICS_HPP
#define TEST_VIRTUAL_PTR_VALUE_SEMANTICS_HPP
#include <boost/openmethod.hpp>
#include <boost/openmethod/policies/vptr_map.hpp>
#include <boost/openmethod/compiler.hpp>
#include <boost/openmethod/unique_ptr.hpp>
using namespace boost::openmethod;
using namespace boost::openmethod::policies;
struct Animal {
virtual ~Animal() {
}
Animal() = default;
Animal(const Animal&) = delete;
};
struct Cat : virtual Animal {};
struct Dog : Animal {};
template<class Policy>
void init_test() {
BOOST_OPENMETHOD_REGISTER(use_classes<Animal, Cat, Dog, Policy>);
struct id;
(void)&method<id(virtual_ptr<Animal, Policy>), void, Policy>::fn;
boost::openmethod::initialize<Policy>();
}
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>> {};
struct direct_map_policy : default_policy::fork<direct_map_policy>::replace<
extern_vptr, 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>> {};
using test_policies = boost::mp11::mp_list<
direct_vector_policy, indirect_vector_policy, direct_map_policy,
indirect_map_policy>;
template<
template<class... Class> class smart_ptr,
template<class... Class> class other_smart_ptr, class Policy>
struct check_illegal_smart_ops {
// a virtual_ptr cannot be constructed from a smart_ptr to a different class
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Cat>, Policy>, smart_ptr<Dog>>);
// a virtual_ptr cannot be constructed from const smart_ptr
static_assert(
!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, smart_ptr<const Animal>>);
// policies must be the same
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, policies::debug>,
virtual_ptr<smart_ptr<Animal>, policies::release>>);
// a smart virtual_ptr cannot be constructed from a plain reference or
// pointer
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, Animal&>);
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>, Animal*>);
static_assert(
!std::is_constructible_v<smart_ptr<Animal>, const other_smart_ptr<Animal>&>);
// smart_ptr<Animal> p{other_smart_ptr<Animal>()};
static_assert(!std::is_constructible_v<
virtual_ptr<smart_ptr<Animal>, Policy>,
virtual_ptr<Animal, Policy>>);
// ---------------------
// test other properties
static_assert(virtual_ptr<smart_ptr<Animal>, Policy>::is_smart_ptr);
static_assert(virtual_ptr<smart_ptr<const Animal>, Policy>::is_smart_ptr);
static_assert(std::is_same_v<
typename virtual_ptr<smart_ptr<Animal>, Policy>::element_type,
Animal>);
static_assert(
std::is_same_v<
decltype(std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()
.get()),
Animal*>);
static_assert(
std::is_same_v<
decltype(*std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()),
Animal&>);
static_assert(
std::is_same_v<
decltype(std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()
.pointer()),
const smart_ptr<Animal>&>);
static_assert(
std::is_same_v<
decltype(*std::declval<virtual_ptr<smart_ptr<Animal>, Policy>>()),
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