mirror of
https://github.com/boostorg/openmethod.git
synced 2026-01-19 04:22:12 +00:00
make virtual_ptr more pointer-like
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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++
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
273
test/test_shared_virtual_ptr_value_semantics.cpp
Normal file
273
test/test_shared_virtual_ptr_value_semantics.cpp
Normal 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>;
|
||||
256
test/test_unique_virtual_ptr_value_semantics.cpp
Normal file
256
test/test_unique_virtual_ptr_value_semantics.cpp
Normal 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>;
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
318
test/test_virtual_ptr_value_semantics.cpp
Normal file
318
test/test_virtual_ptr_value_semantics.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
125
test/test_virtual_ptr_value_semantics.hpp
Normal file
125
test/test_virtual_ptr_value_semantics.hpp
Normal 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
|
||||
Reference in New Issue
Block a user