diff --git a/doc/virtual_.adoc b/doc/virtual_.adoc index 6ef8d6e..5cc8c83 100644 --- a/doc/virtual_.adoc +++ b/doc/virtual_.adoc @@ -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`, `std::shared_ptr` and `const +`T&`, `T&&`, `T*`, `std::unique_ptr`, `std::shared_ptr` and `const std::shared_ptr&` are provided. See the documentation of `virtual_traits` for more information. diff --git a/doc/virtual_ptr.adoc b/doc/virtual_ptr.adoc index 47e872d..056d605 100644 --- a/doc/virtual_ptr.adoc +++ b/doc/virtual_ptr.adoc @@ -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 virtual_ptr(Other& other); template virtual_ptr(const Other& other); template virtual_ptr(Other&& other); + virtual_ptr& operator =(nullptr_t); + template virtual_ptr& operator =(Other& other); + template virtual_ptr& operator =(const Other& other); + template virtual_ptr& operator =(Other&& other); + template 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 virtual_ptr(Other& other); // 1 -template virtual_ptr(const Other& other); // 2 -template virtual_ptr(Other&& other); // 3 +virtual_ptr(); // 1 +virtual_ptr(nullptr_t); // 2 +template virtual_ptr(Other& other); // 3 +template virtual_ptr(const Other& other); // 4 +template virtual_ptr(Other&& other); // 5 +template 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 virtual_ptr& operator =(Other& other); // 2 +template virtual_ptr& operator =(const Other& other); // 3 +template virtual_ptr& operator =(Other&& other); // 4 +template 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++ diff --git a/doc/virtual_traits.adoc b/doc/virtual_traits.adoc index 1b786b3..5b1e56c 100644 --- a/doc/virtual_traits.adoc +++ b/doc/virtual_traits.adoc @@ -35,6 +35,7 @@ Specializations are provided for: * `const virtual_ptr&` * `T&` * `T&&` +* `T*` * `std::shared_ptr`: defined in * `const std::shared_ptr&`: defined in * `std::unique_ptr`: defined in diff --git a/examples/slides.cpp b/examples/slides.cpp index 3061017..e417b47 100644 --- a/examples/slides.cpp +++ b/examples/slides.cpp @@ -232,7 +232,7 @@ BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr expr), int) { return expr->val; } -BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr expr), int) { +BOOST_OPENMETHOD_OVERRIDE(value, (virtual_ptr expr), int) { return value(expr->left) * value(expr->right); } diff --git a/examples/synopsis.cpp b/examples/synopsis.cpp index c4c7d26..63117f4 100644 --- a/examples/synopsis.cpp +++ b/examples/synopsis.cpp @@ -81,7 +81,7 @@ BOOST_OPENMETHOD_OVERRIDE(meet, (virtual_ptr cat, virtual_ptr dog, std #include auto main() -> int { - // Initialise method dispatch tables. + // Initialize the dispatch tables. boost::openmethod::initialize(); // Create a few objects. diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index 4242d7a..f92b573 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -255,10 +255,25 @@ struct virtual_traits { } }; -// For covariant return types. template struct virtual_traits { using virtual_type = std::remove_cv_t; + + static auto peek(T* arg) -> const T& { + return *arg; + } + + template + static auto cast(T* ptr) { + static_assert( + std::is_base_of_v< + virtual_type, std::remove_pointer_t>>); + if constexpr (detail::requires_dynamic_cast) { + return dynamic_cast(ptr); + } else { + return static_cast(ptr); + } + } }; template @@ -303,6 +318,25 @@ struct is_virtual_ptr_aux&> : std::true_type { template constexpr bool is_virtual_ptr = detail::is_virtual_ptr_aux::value; +template +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 virtual_ptr_impl { public: @@ -313,17 +347,44 @@ class virtual_ptr_impl { static constexpr bool use_indirect_vptrs = Policy::template has_facet; - template - 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(null_vptr)) { } - template + template< + class Other, + typename = std::enable_if_t>> + virtual_ptr_impl(Other& other) + : obj(&other), + vp(box_vptr(Policy::dynamic_vptr(other))) { + } + + template< + class Other, + typename = std::enable_if_t>().get())>>> + virtual_ptr_impl(Other* other) + : obj(other), + vp(box_vptr(Policy::dynamic_vptr(*other))) { + } + + template< + class Other, + typename = std::enable_if_t>().get())>>> virtual_ptr_impl(const virtual_ptr& other) : obj(other.get()), vp(other.vp) { } - template + template< + class Other, + typename = std::enable_if_t>().get())>>> virtual_ptr_impl(virtual_ptr& 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 - virtual_ptr_impl(Other& other, const vptr_type& vp) : obj(&other), vp(vp) { + template< + class Other, + typename = std::enable_if_t>> + virtual_ptr_impl(Other& other, const vptr_type& vp) + : obj(&other), vp(box_vptr(vp)) { + } + + template< + class Other, + typename = std::enable_if_t>> + virtual_ptr_impl& operator=(Other& other) { + obj = &other; + vp = box_vptr(Policy::dynamic_vptr(other)); + return *this; + } + + template< + class Other, + typename = std::enable_if_t>> + virtual_ptr_impl& operator=(Other* other) { + obj = other; + vp = box_vptr(Policy::dynamic_vptr(*other)); + return *this; + } + + template< + class Other, + typename = std::enable_if_t>().get())>>> + virtual_ptr_impl& operator=(const virtual_ptr_impl& other) { + obj = other.get(); + vp = other.vp; + return *this; + } + + virtual_ptr_impl& operator=(std::nullptr_t) { + obj = nullptr; + vp = box_vptr(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 || std::is_base_of_v); return virtual_ptr( - traits::template cast(*obj), vp); + traits::template cast(*obj), unbox_vptr(vp)); } template friend struct virtual_traits; protected: - std::conditional_t vp; + std::conditional_t 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::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 +struct same_smart_ptr_aux : std::false_type {}; template -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::template rebind< + typename Other::element_type>>> + : std::is_same< + Other, + typename virtual_traits::template rebind< + typename Other::element_type>> {}; + +template +constexpr bool same_smart_ptr = same_smart_ptr_aux::value; template class virtual_ptr_impl< Class, Policy, std::void_t< typename virtual_traits::template rebind>> { + public: using traits = virtual_traits; using element_type = typename Class::element_type; template friend class virtual_ptr; - + template + friend class virtual_ptr_impl; template friend struct virtual_traits; @@ -409,50 +509,146 @@ class virtual_ptr_impl< static constexpr bool use_indirect_vptrs = Policy::template has_facet; - std::conditional_t vp; + std::conditional_t vp; Class obj; public: static constexpr bool is_smart_ptr = true; + virtual_ptr_impl() : vp(box_vptr(null_vptr)) { + } + + explicit virtual_ptr_impl(std::nullptr_t) + : obj(nullptr), vp(box_vptr(null_vptr)) { + } + + virtual_ptr_impl(const virtual_ptr_impl& other) = default; + template< class Other, - typename = - typename enable_if_compatible_smart_ptr::type> + typename = std::enable_if_t< + same_smart_ptr && + std::is_constructible_v>> virtual_ptr_impl(const Other& other) - : obj(other), vp(Policy::dynamic_vptr(*other)) { + : obj(other), vp(box_vptr( + other ? Policy::dynamic_vptr(*other) : null_vptr)) { } template< class Other, - typename = - typename enable_if_compatible_smart_ptr::type> + typename = std::enable_if_t< + same_smart_ptr && + std::is_constructible_v>> + virtual_ptr_impl(Other& other) + : obj(other), vp(box_vptr( + other ? Policy::dynamic_vptr(*other) : null_vptr)) { + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_constructible_v>> virtual_ptr_impl(Other&& other) - : obj(std::move(other)), vp(Policy::dynamic_vptr(*other)) { + : obj(std::move(other)), + vp(box_vptr( + other ? Policy::dynamic_vptr(*other) : null_vptr)) { } template< class Other, - typename = - typename enable_if_compatible_smart_ptr::type> - virtual_ptr_impl(virtual_ptr& other) - : obj(other.obj), vp(other.vp) { - } - - template< - class Other, - typename = - typename enable_if_compatible_smart_ptr::type> + typename = std::enable_if_t< + same_smart_ptr && + std::is_constructible_v>> virtual_ptr_impl(const virtual_ptr& other) : obj(other.obj), vp(other.vp) { } template< class Other, - typename = - typename enable_if_compatible_smart_ptr::type> + typename = std::enable_if_t< + same_smart_ptr && + std::is_constructible_v>> + virtual_ptr_impl(virtual_ptr& 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(null_vptr); + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_constructible_v>> virtual_ptr_impl(virtual_ptr&& other) : obj(std::move(other.obj)), vp(other.vp) { + other.vp = box_vptr(null_vptr); + } + + virtual_ptr_impl& operator=(std::nullptr_t) { + obj = nullptr; + vp = box_vptr(null_vptr); + return *this; + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_assignable_v>> + virtual_ptr_impl& operator=(const Other& other) { + obj = other; + vp = box_vptr(Policy::dynamic_vptr(*other)); + return *this; + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_assignable_v>> + virtual_ptr_impl& operator=(Other&& other) { + vp = box_vptr( + other ? Policy::dynamic_vptr(*other) : null_vptr); + obj = std::move(other); + return *this; + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_assignable_v>> + virtual_ptr_impl& operator=(virtual_ptr& other) { + obj = other.obj; + vp = other.vp; + return *this; + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_assignable_v>> + virtual_ptr_impl& operator=(const virtual_ptr_impl& other) { + obj = other.obj; + vp = other.vp; + return *this; + } + + template< + class Other, + typename = std::enable_if_t< + same_smart_ptr && + std::is_assignable_v>> + virtual_ptr_impl& operator=(virtual_ptr_impl&& other) { + obj = std::move(other.obj); + vp = other.vp; + other.vp = box_vptr(null_vptr); + return *this; } auto get() const -> element_type* { @@ -472,8 +668,8 @@ class virtual_ptr_impl< } template - virtual_ptr_impl(Arg&& obj, const vptr_type& vp) - : obj(std::forward(obj)), vp(vp) { + virtual_ptr_impl(Arg&& obj, decltype(vp) other_vp) + : obj(std::forward(obj)), vp(other_vp) { } template @@ -526,6 +722,14 @@ class virtual_ptr : public detail::virtual_ptr_impl { template friend class detail::virtual_ptr_impl; + template< + typename Other, + typename = std::enable_if_t>> + virtual_ptr& operator=(Other&& other) { + impl::operator=(std::forward(other)); + return *this; + } + template static auto final(Other&& obj) { using other_traits = virtual_traits; @@ -550,11 +754,12 @@ class virtual_ptr : public detail::virtual_ptr_impl { return virtual_ptr( std::forward(obj), - Policy::template static_vptr); + detail::box_vptr( + Policy::template static_vptr)); } auto vptr() const { - return this->vp; + return detail::unbox_vptr(this->vp); } }; diff --git a/include/boost/openmethod/policies/fast_perfect_hash.hpp b/include/boost/openmethod/policies/fast_perfect_hash.hpp index 878aec0..e30b588 100644 --- a/include/boost/openmethod/policies/fast_perfect_hash.hpp +++ b/include/boost/openmethod/policies/fast_perfect_hash.hpp @@ -139,14 +139,12 @@ void fast_perfect_hash::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::hash_initialize( template void fast_perfect_hash::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[index] != type) { if constexpr (Policy::template has_facet) { unknown_class_error error; diff --git a/include/boost/openmethod/policies/vptr_vector.hpp b/include/boost/openmethod/policies/vptr_vector.hpp index 8c8da69..4d12071 100644 --- a/include/boost/openmethod/policies/vptr_vector.hpp +++ b/include/boost/openmethod/policies/vptr_vector.hpp @@ -37,7 +37,7 @@ class vptr_vector : public extern_vptr, if constexpr (Policy::template has_facet) { auto report = Policy::hash_initialize(first, last); - size = report.last; + size = report.last + 1; } else { size = 0; diff --git a/test/Jamfile b/test/Jamfile index 52f2fda..ad6663b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -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 ; diff --git a/test/test_blackbox.cpp b/test/test_blackbox.cpp index ba947f4..588c37b 100644 --- a/test/test_blackbox.cpp +++ b/test/test_blackbox.cpp @@ -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_), 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(); + + 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 { // ----------------------------------------------------------------------------- diff --git a/test/test_core.cpp b/test/test_core.cpp index 894bc59..3c261f8 100644 --- a/test/test_core.cpp +++ b/test/test_core.cpp @@ -7,6 +7,7 @@ #include #include +#include #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::virtual_type, base>); + std::is_same_v::virtual_type, base>); + +static_assert(std::is_same_v< + virtual_traits::virtual_type, base>); + +static_assert( + std::is_same_v::virtual_type, base>); + +static_assert( + std::is_same_v::virtual_type, void>); + +static_assert(std::is_same_v< + boost::mp11::mp_filter< + is_virtual, mp11::mp_list, b, virtual_>>, + mp11::mp_list, virtual_>>); + +static_assert(std::is_same_v>, a&>); + +static_assert(std::is_same_v, a>); static_assert( std::is_same_v< - virtual_traits::virtual_type, base>); + virtual_types>, b, virtual_>>>, + mp11::mp_list, std::shared_ptr>>); + +static_assert(std::is_same_v< + overrider_virtual_types< + mp11::mp_list, b, virtual_>, + mp11::mp_list, default_policy>, + mp11::mp_list>); + +static_assert( + std::is_same_v, default_policy>, a>); + +static_assert(std::is_same_v< + virtual_traits, default_policy>::virtual_type, a>); + +static_assert(std::is_same_v< + select_overrider_virtual_type_aux< + virtual_ptr, virtual_ptr, default_policy>::type, + a>); static_assert( std::is_same_v< - virtual_traits::virtual_type, base>); + overrider_virtual_types< + mp11::mp_list, b, virtual_ptr>, + mp11::mp_list, e, virtual_ptr>, default_policy>, + mp11::mp_list>); static_assert( std::is_same_v< - virtual_traits::virtual_type, void>); + overrider_virtual_types< + mp11::mp_list< + const virtual_ptr&, b, const virtual_ptr&>, + mp11::mp_list&, e, const virtual_ptr&>, + default_policy>, + mp11::mp_list>); static_assert( std::is_same_v< - boost::mp11::mp_filter< - is_virtual, - mp11::mp_list< virtual_, b, virtual_ > - >, - mp11::mp_list< virtual_, virtual_ > - >); + overrider_virtual_types< + mp11::mp_list< + virtual_>, b, virtual_>>, + mp11::mp_list, e, std::shared_ptr>, + default_policy>, + mp11::mp_list>); -static_assert( - std::is_same_v< - remove_virtual>, - a& - >); - -static_assert( - std::is_same_v< - virtual_type, - a - >); - -static_assert( - std::is_same_v< - boost::mp11::mp_transform< - remove_virtual, - mp11::mp_list< virtual_, virtual_ > - >, - mp11::mp_list - >); +static_assert(std::is_same_v< + boost::mp11::mp_transform< + remove_virtual, mp11::mp_list, virtual_>>, + mp11::mp_list>); static_assert( std::is_same_v< boost::mp11::mp_transform_q< boost::mp11::mp_bind_back, boost::mp11::mp_transform< - remove_virtual, - mp11::mp_list< virtual_, virtual_ > - > - >, - mp11::mp_list - >); + remove_virtual, mp11::mp_list, virtual_>>>, + mp11::mp_list>); 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_, b, virtual_ > - > - > - >, - mp11::mp_list - >); + is_virtual, mp11::mp_list, b, virtual_>>>>, + mp11::mp_list>); // clang-format on diff --git a/test/test_shared_virtual_ptr_value_semantics.cpp b/test/test_shared_virtual_ptr_value_semantics.cpp new file mode 100644 index 0000000..8d38cb3 --- /dev/null +++ b/test/test_shared_virtual_ptr_value_semantics.cpp @@ -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 + +#define BOOST_TEST_MODULE openmethod +#include + +#include "test_virtual_ptr_value_semantics.hpp" + +#include + +static_assert(detail::same_smart_ptr< + std::shared_ptr, std::shared_ptr, default_policy>); + +static_assert(!detail::same_smart_ptr< + std::shared_ptr, std::unique_ptr, default_policy>); + +static_assert(!detail::same_smart_ptr< + std::shared_ptr, shared_virtual_ptr>, + default_policy>); + +BOOST_AUTO_TEST_CASE_TEMPLATE(shared_virtual_ptr_value, Policy, test_policies) { + static_assert( + std::is_same_v< + typename shared_virtual_ptr::element_type, Animal>); + static_assert( + std::is_same_v< + decltype(std::declval>().get()), + Animal*>); + static_assert(shared_virtual_ptr::is_smart_ptr); + static_assert(shared_virtual_ptr::is_smart_ptr); + static_assert(std::is_same_v< + decltype(*std::declval>()), + Animal&>); + + init_test(); + + // construction and assignment from a plain pointer or reference is not + // allowed + + static_assert( + !construct_assign_ok, Dog>); + static_assert( + !construct_assign_ok, Dog&&>); + static_assert( + !construct_assign_ok, const Dog&>); + static_assert( + !construct_assign_ok, const Dog*>); + + // ------------------------------------------------------------------------- + // construction and assignment from plain references and pointers + + { + shared_virtual_ptr p{nullptr}; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto hector = std::make_shared(); + p = hector; + BOOST_TEST(p.get() == hector.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto felix = std::make_shared(); + p = felix; + BOOST_TEST(p.get() == felix.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto hector = std::make_shared(); + p = hector; + BOOST_TEST(p.get() == hector.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto felix = std::make_shared(); + p = felix; + BOOST_TEST(p.get() == felix.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto hector = std::make_shared(); + p = hector; + BOOST_TEST(p.get() == hector.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto felix = std::make_shared(); + p = felix; + BOOST_TEST(p.get() == felix.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto hector = std::make_shared(); + p = hector; + BOOST_TEST(p.get() == hector.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + auto felix = std::make_shared(); + p = felix; + BOOST_TEST(p.get() == felix.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + // shared_virtual_ptr p{Dog()}; + static_assert(!construct_assign_ok, Dog&&>); + + // ------------------------------------------------------------------------- + // construction and assignment from other shared_virtual_ptr + + { + // shared_virtual_ptr(const shared_virtual_ptr&) + auto snoopy = std::make_shared(); + const shared_virtual_ptr p(snoopy); + shared_virtual_ptr q(p); + BOOST_TEST(q.get() == snoopy.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + } + + { + // shared_virtual_ptr(shared_virtual_ptr&) + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + shared_virtual_ptr q(p); + BOOST_TEST(q.get() == snoopy.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + } + + { + // shared_virtual_ptr(shared_virtual_ptr&&) + auto snoopy = std::make_shared(); + shared_virtual_ptr p(snoopy); + shared_virtual_ptr q(std::move(p)); + BOOST_TEST(q.get() == snoopy.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + // shared_virtual_ptr(const shared_virtual_ptr&) + auto snoopy = std::make_shared(); + const shared_virtual_ptr p(snoopy); + shared_virtual_ptr base(p); + BOOST_TEST(base.get() == snoopy.get()); + BOOST_TEST(base.vptr() == Policy::template static_vptr); + } + + { + // shared_virtual_ptr(const shared_virtual_ptr&) + auto snoopy = std::make_shared(); + const shared_virtual_ptr p(snoopy); + shared_virtual_ptr const_q(p); + BOOST_TEST(const_q.get() == snoopy.get()); + BOOST_TEST(const_q.vptr() == Policy::template static_vptr); + } + + { + // shared_virtual_ptr(const shared_virtual_ptr&) + auto snoopy = std::make_shared(); + const shared_virtual_ptr p(snoopy); + shared_virtual_ptr const_base_q(p); + BOOST_TEST(const_base_q.get() == snoopy.get()); + BOOST_TEST(const_base_q.vptr() == Policy::template static_vptr); + } + + { + // shared_virtual_ptr() + shared_virtual_ptr p{nullptr}; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + shared_virtual_ptr p{std::shared_ptr()}; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + // ------------------------------------------------------------------------- + // assignment + + { + shared_virtual_ptr p; + auto snoopy = std::make_shared(); + p = snoopy; + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + shared_virtual_ptr p; + auto snoopy = std::make_shared(); + p = snoopy; + BOOST_TEST(p.get() == snoopy.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + auto p = make_shared_virtual(); + p = nullptr; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + auto p = make_shared_virtual(); + p = std::shared_ptr(); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + static_assert( + !construct_assign_ok, const Dog&>); + static_assert( + !construct_assign_ok, const Dog*>); +} + +template struct check_illegal_smart_ops< + std::shared_ptr, std::unique_ptr, direct_vector_policy>; diff --git a/test/test_unique_virtual_ptr_value_semantics.cpp b/test/test_unique_virtual_ptr_value_semantics.cpp new file mode 100644 index 0000000..3b50813 --- /dev/null +++ b/test/test_unique_virtual_ptr_value_semantics.cpp @@ -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 + +#define BOOST_TEST_MODULE openmethod +#include + +#include "test_virtual_ptr_value_semantics.hpp" + +#include + +BOOST_AUTO_TEST_CASE_TEMPLATE(unique_virtual_ptr_value, Policy, test_policies) { + init_test(); + + static_assert( + std::is_same_v< + typename unique_virtual_ptr::element_type, Animal>); + static_assert( + std::is_same_v< + decltype(std::declval>().get()), + Animal*>); + static_assert(unique_virtual_ptr::is_smart_ptr); + static_assert(unique_virtual_ptr::is_smart_ptr); + static_assert(std::is_same_v< + decltype(*std::declval>()), + Animal&>); + + { + // unique_virtual_ptr(nullptr) + unique_virtual_ptr p{nullptr}; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + static_assert(!construct_assign_ok, Dog>); + + static_assert(!construct_assign_ok, Dog&>); + + static_assert(!construct_assign_ok, Dog*>); + + static_assert(!construct_assign_ok< + unique_virtual_ptr, std::unique_ptr&>); + + static_assert( + !construct_assign_ok< + unique_virtual_ptr, const std::unique_ptr&>); + + static_assert(!construct_assign_ok< + unique_virtual_ptr, unique_virtual_ptr>); + + static_assert(!construct_assign_ok< + unique_virtual_ptr, unique_virtual_ptr&>); + + static_assert( + !construct_assign_ok< + unique_virtual_ptr, const unique_virtual_ptr&>); + + { + // construct from unique_ptr temporary + unique_virtual_ptr p(std::make_unique()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + // derived-to-base ok? + unique_virtual_ptr p(std::make_unique()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + static_assert( + !construct_assign_ok< + unique_virtual_ptr, const std::unique_ptr&>); + + static_assert(!construct_assign_ok< + unique_virtual_ptr, std::unique_ptr&>); + + static_assert(!construct_assign_ok< + unique_virtual_ptr, + const unique_virtual_ptr&>); + + static_assert( + !construct_assign_ok< + unique_virtual_ptr, unique_virtual_ptr&>); + + static_assert( + !construct_assign_ok< + unique_virtual_ptr, unique_virtual_ptr&>); + + { + // assign from smart ptr temporary + unique_virtual_ptr p{nullptr}; + p = std::make_unique(); + BOOST_TEST(p.get() != nullptr); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + // derived-to-base ok? + unique_virtual_ptr p{nullptr}; + p = std::make_unique(); + BOOST_TEST(p.get() != nullptr); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + // unique_virtual_ptr(unique_virtual_ptr) + unique_virtual_ptr p(std::make_unique()); + auto dog = p.get(); + unique_virtual_ptr q(std::move(p)); + BOOST_TEST(q.get() == dog); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + // derived-to-base ok? + unique_virtual_ptr p(std::make_unique()); + auto dog = p.get(); + unique_virtual_ptr q(std::move(p)); + BOOST_TEST(q.get() == dog); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + // virtual_ptr(std::unique_ptr()) + unique_virtual_ptr p = std::unique_ptr(); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + unique_virtual_ptr p(std::make_unique()); + p = nullptr; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + unique_virtual_ptr p(std::make_unique()); + p = std::unique_ptr(); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + +#if 0 + + { + // unique_virtual_ptr = const std::unique_ptr& + unique_virtual_ptr p; + const auto s = std::make_unique(); + p = s; + BOOST_TEST(p.get() == s.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + // unique_virtual_ptr = std::unique_ptr& + unique_virtual_ptr p; + auto s = std::make_unique(); + p = s; + BOOST_TEST(p.get() == s.get()); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + // unique_virtual_ptr = std::unique_ptr&& + auto s = std::make_unique(); + auto p = s; + unique_virtual_ptr q; + q = std::move(p); + BOOST_TEST(q.get() == s.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + } + + { + // unique_virtual_ptr = std::unique_ptr&& + auto s = std::make_unique(); + auto p = s; + unique_virtual_ptr q; + q = std::move(p); + BOOST_TEST(q.get() == s.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + } + + { + // unique_virtual_ptr = unique_virtual_ptr&& + auto s = std::make_unique(); + unique_virtual_ptr p(s); + unique_virtual_ptr q; + q = std::move(p); + BOOST_TEST(q.get() == s.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + // unique_virtual_ptr = unique_virtual_ptr&& + auto s = std::make_unique(); + unique_virtual_ptr p(s); + unique_virtual_ptr q; + q = std::move(p); + BOOST_TEST(q.get() == s.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + // virtual_ptr(unique_virtual_ptr&) + auto p = make_unique_virtual(); + virtual_ptr q(p); + BOOST_TEST(q.get() == p.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr = unique_virtual_ptr& + const auto p = make_unique_virtual(); + virtual_ptr q; + q = p; + BOOST_TEST(q.get() == p.get()); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + } + + // illegal constructions and assignments + static_assert( + !construct_assign_ok, Dog>); + static_assert( + !construct_assign_ok, Dog&&>); + static_assert( + !construct_assign_ok, const Dog&>); + static_assert( + !construct_assign_ok, const Dog*>); + + static_assert(!std::is_assignable_v, Dog>); + static_assert( + !std::is_assignable_v, Dog&&>); + static_assert( + !std::is_assignable_v, const Dog&>); + static_assert( + !std::is_assignable_v, const Dog*>); + +#endif +} + +template struct check_illegal_smart_ops< + std::unique_ptr, std::shared_ptr, direct_vector_policy>; diff --git a/test/test_virtual_ptr_basic.cpp b/test/test_virtual_ptr_basic.cpp deleted file mode 100644 index 534a2c2..0000000 --- a/test/test_virtual_ptr_basic.cpp +++ /dev/null @@ -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 -#include -#include -#include - -#include -#include -#include - -#define BOOST_TEST_MODULE openmethod -#include -#include - -#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>, b, virtual_>>>, - mp_list, std::shared_ptr>>); - -static_assert(std::is_same_v< - overrider_virtual_types< - mp_list, b, virtual_>, mp_list, - default_policy>, - mp_list>); - -static_assert( - std::is_same_v, default_policy>, a>); - -static_assert(std::is_same_v< - virtual_traits, default_policy>::virtual_type, a>); - -static_assert(std::is_same_v< - select_overrider_virtual_type_aux< - virtual_ptr, virtual_ptr, default_policy>::type, - a>); - -static_assert(std::is_same_v< - overrider_virtual_types< - mp_list, b, virtual_ptr>, - mp_list, e, virtual_ptr>, default_policy>, - mp_list>); - -static_assert( - std::is_same_v< - overrider_virtual_types< - mp_list&, b, const virtual_ptr&>, - mp_list&, e, const virtual_ptr&>, - default_policy>, - mp_list>); - -static_assert( - std::is_same_v< - overrider_virtual_types< - mp_list< - virtual_>, b, virtual_>>, - mp_list, e, std::shared_ptr>, default_policy>, - mp_list>); - -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&, std::ostream&), void); - -BOOST_OPENMETHOD_OVERRIDE( - poke, (const virtual_ptr&, std::ostream& os), void) { - os << "bark"; -} - -static_assert(sizeof(virtual_ptr) == 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 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, std::ostream&), void); - -BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr, 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 = make_shared_virtual(); - poke(animal, os); - BOOST_CHECK(os.is_equal("bark")); - } -} - -} // namespace BOOST_OPENMETHOD_GENSYM - -namespace BOOST_OPENMETHOD_GENSYM { - -BOOST_OPENMETHOD( - poke, (const shared_virtual_ptr&, std::ostream&), void); - -BOOST_OPENMETHOD_OVERRIDE( - poke, (const shared_virtual_ptr&, 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 = make_shared_virtual(); - poke(animal, os); - BOOST_CHECK(os.is_equal("bark")); - } -} - -} // namespace BOOST_OPENMETHOD_GENSYM - -namespace BOOST_OPENMETHOD_GENSYM { - -BOOST_OPENMETHOD(poke, (unique_virtual_ptr, std::ostream&), void); - -BOOST_OPENMETHOD_OVERRIDE( - poke, (unique_virtual_ptr, 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(), 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, std::ostream&), void); - -BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr, 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::final(dog); - poke(vptr, os); - BOOST_CHECK(os.is_equal("bark")); - } -} - -} // namespace using_non_polymorphic_classes diff --git a/test/test_virtual_ptr_all.cpp b/test/test_virtual_ptr_dispatch.cpp similarity index 60% rename from test/test_virtual_ptr_all.cpp rename to test/test_virtual_ptr_dispatch.cpp index 6fd26d4..267dc31 100644 --- a/test/test_virtual_ptr_all.cpp +++ b/test/test_virtual_ptr_dispatch.cpp @@ -4,16 +4,171 @@ // or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include #include +#include +#include -#include "test_util.hpp" +#include +#include +#include #define BOOST_TEST_MODULE openmethod #include -#include +#include +#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&, std::ostream&), void); + +BOOST_OPENMETHOD_OVERRIDE( + poke, (const virtual_ptr&, std::ostream& os), void) { + os << "bark"; +} + +static_assert(sizeof(virtual_ptr) == 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 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, std::ostream&), void); + +BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr, 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 = make_shared_virtual(); + poke(animal, os); + BOOST_CHECK(os.is_equal("bark")); + } +} + +} // namespace BOOST_OPENMETHOD_GENSYM + +namespace BOOST_OPENMETHOD_GENSYM { + +BOOST_OPENMETHOD( + poke, (const shared_virtual_ptr&, std::ostream&), void); + +BOOST_OPENMETHOD_OVERRIDE( + poke, (const shared_virtual_ptr&, 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 = make_shared_virtual(); + poke(animal, os); + BOOST_CHECK(os.is_equal("bark")); + } +} + +} // namespace BOOST_OPENMETHOD_GENSYM + +namespace BOOST_OPENMETHOD_GENSYM { + +BOOST_OPENMETHOD(poke, (unique_virtual_ptr, std::ostream&), void); + +BOOST_OPENMETHOD_OVERRIDE( + poke, (unique_virtual_ptr, 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(), 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, std::ostream&), void); + +BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr, 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::final(dog); + poke(vptr, os); + BOOST_CHECK(os.is_equal("bark")); + } +} + +} // namespace using_non_polymorphic_classes + struct Player { virtual ~Player() { diff --git a/test/test_virtual_ptr_state.cpp b/test/test_virtual_ptr_state.cpp deleted file mode 100644 index f5cdacf..0000000 --- a/test/test_virtual_ptr_state.cpp +++ /dev/null @@ -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 -#include -#include - -#define BOOST_TEST_MODULE openmethod -#include - -#include -#include - -#include -#include -#include -#include -#include - -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 -void init_test() { - BOOST_OPENMETHOD_REGISTER(use_classes); - struct id; - (void)&method), void, Policy>::fn; - boost::openmethod::initialize(); -} - -struct direct_vector_policy : default_policy::fork {}; - -struct indirect_vector_policy - : default_policy::fork::replace< - extern_vptr, vptr_vector> {}; - -struct direct_map_policy : default_policy::fork::replace< - extern_vptr, vptr_map> {}; - -struct indirect_map_policy - : default_policy::fork::replace< - extern_vptr, vptr_map> {}; - -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::element_type, Animal>); - static_assert(std::is_same_v< - decltype(std::declval>().get()), - Animal*>); - static_assert(!virtual_ptr::is_smart_ptr); - static_assert(!virtual_ptr::is_smart_ptr); - static_assert( - std::is_same_v< - decltype(*std::declval>()), Animal&>); - static_assert(!std::is_constructible_v, Dog>); - - init_test(); - - { - Dog dog; - - { - virtual_ptr p(dog); - BOOST_TEST(p.vptr() != nullptr); - BOOST_TEST(p.vptr() == Policy::template static_vptr); - virtual_ptr copy(p); - virtual_ptr base(p); - virtual_ptr const_copy(p); - virtual_ptr const_base_copy(p); - } - - { - const auto p = virtual_ptr(dog); - virtual_ptr const_copy(p); - virtual_ptr const_base_copy(p); - } - - { - auto p = virtual_ptr(dog); - } - } -} - -template< - template class smart_ptr, - template 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, Policy>, smart_ptr>); - - // a virtual_ptr to const can be constructed from smart_ptr (same class) - static_assert( - std::is_constructible_v< - virtual_ptr, Policy>, const smart_ptr&>); - - static_assert( - std::is_constructible_v< - virtual_ptr, Policy>, smart_ptr>); - - // a virtual_ptr can be constructed from a smart_ptr (derived class) - static_assert(std::is_constructible_v< - virtual_ptr, Policy>, smart_ptr>); - - // a virtual_ptr to const can be constructed from a smart_ptr (derived class) - static_assert( - std::is_constructible_v< - virtual_ptr, Policy>, smart_ptr>); - - // a virtual_ptr cannot be constructed from a smart_ptr to a different class - static_assert(!std::is_constructible_v< - virtual_ptr, Policy>, smart_ptr>); - - // a virtual_ptr cannot be constructed from const smart_ptr - static_assert( - !std::is_constructible_v< - virtual_ptr, Policy>, smart_ptr>); - - // policies must be the same - static_assert(!std::is_constructible_v< - virtual_ptr, policies::debug>, - virtual_ptr, policies::release>>); - - // move constructible - static_assert( - std::is_move_constructible_v, Policy>>); - - // a smart virtual_ptr cannot be constructed from a plain reference or - // pointer - static_assert(!std::is_constructible_v< - virtual_ptr, Policy>, Animal>); - static_assert(!std::is_constructible_v< - virtual_ptr, Policy>, Animal*>); - - // the smart pointer must be the same (e.g. both shared_ptr) - static_assert(!std::is_constructible_v< - virtual_ptr, Policy>, - virtual_ptr, Policy>>); - - // a smart virtual_ptr converts to a plain one - static_assert(std::is_constructible_v< - virtual_ptr, - virtual_ptr, Policy>>); - - // but not the other way around - static_assert(!std::is_constructible_v< - virtual_ptr, Policy>, - virtual_ptr>); - - // --------------------- - // test other properties - - static_assert(virtual_ptr, Policy>::is_smart_ptr); - static_assert(virtual_ptr, Policy>::is_smart_ptr); - - static_assert(std::is_same_v< - typename virtual_ptr, Policy>::element_type, - Animal>); - - static_assert( - std::is_same_v< - decltype(std::declval, Policy>>() - .get()), - Animal*>); - - static_assert( - std::is_same_v< - decltype(*std::declval, Policy>>()), - Animal&>); - - static_assert( - std::is_same_v< - decltype(std::declval, Policy>>() - .pointer()), - const smart_ptr&>); - - static_assert( - std::is_same_v< - decltype(*std::declval, 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(); - - auto dog = std::make_shared(); - - { - shared_virtual_ptr ptr(dog); - BOOST_TEST((ptr.get() == dog.get())); - BOOST_TEST(ptr.pointer() == dog); - BOOST_TEST(ptr.vptr() == Policy::template static_vptr); - - shared_virtual_ptr copy(ptr); - BOOST_TEST((copy.get() == dog.get())); - BOOST_TEST(copy.pointer() == dog); - BOOST_TEST(copy.vptr() == Policy::template static_vptr); - - shared_virtual_ptr base(ptr); - BOOST_TEST((base.get() == dog.get())); - - shared_virtual_ptr downcast = - base.template cast(); - BOOST_TEST((downcast.get() == dog.get())); - BOOST_TEST(base.vptr() == Policy::template static_vptr); - - shared_virtual_ptr const_copy(ptr); - shared_virtual_ptr base_const_copy(ptr); - - shared_virtual_ptr move_ptr(std::move(ptr)); - BOOST_TEST(ptr.pointer().get() == nullptr); - BOOST_TEST(move_ptr.pointer().get() == dog.get()); - } - - { - shared_virtual_ptr ptr(std::make_shared()); - virtual_ptr dumb_vptr(ptr); - BOOST_TEST(dumb_vptr.get() == ptr.get()); - BOOST_TEST(dumb_vptr.vptr() == ptr.vptr()); - } - - { - shared_virtual_ptr ptr(std::make_shared()); - virtual_ptr dumb_vptr(ptr); - BOOST_TEST(dumb_vptr.get() == ptr.get()); - BOOST_TEST(dumb_vptr.vptr() == ptr.vptr()); - } - - { - shared_virtual_ptr ptr(dog); - shared_virtual_ptr move_const_ptr( - std::move(ptr)); - BOOST_TEST(ptr.pointer().get() == nullptr); - BOOST_TEST(move_const_ptr.pointer().get() == dog.get()); - } - - { - shared_virtual_ptr ptr(dog); - } - - { - // should not compile: - // unique_virtual_ptr unique_dog(dog); - } - } - - { - auto dog = std::make_shared(); - - shared_virtual_ptr ptr(dog); - BOOST_TEST(ptr.pointer().get() == dog.get()); - BOOST_TEST(ptr.vptr() == Policy::template static_vptr); - - shared_virtual_ptr copy(ptr); - shared_virtual_ptr base(ptr); - } -} - -BOOST_AUTO_TEST_CASE_TEMPLATE(unique_virtual_ptr_ctors, Policy, test_policies) { - init_test(); - - { - // a unique_virtual_ptr can be created from a std::unique_ptr - std::unique_ptr is_smart_ptr = std::make_unique(); - auto dumb_ptr = is_smart_ptr.get(); - unique_virtual_ptr 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(); - auto dumb_ptr = virtual_smart_ptr.get(); - virtual_ptr 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* dumb_ptr = unique.get(); - unique_virtual_ptr unique_moved = std::move(unique); - BOOST_TEST(unique.get() == nullptr); - BOOST_TEST(unique_moved.get() == dumb_ptr); - } - - { - unique_virtual_ptr base(std::make_unique()); - auto p = base.get(); - unique_virtual_ptr downcast = - std::move(base).template cast(); - BOOST_TEST((downcast.get() == p)); - BOOST_TEST((base.get() == nullptr)); - BOOST_TEST(downcast.vptr() == Policy::template static_vptr); - } -} - -BOOST_AUTO_TEST_CASE_TEMPLATE(indirect_virtual_ptr, Policy, test_policies) { - BOOST_TEST_MESSAGE( - "Policy = " << boost::core::demangle(typeid(Policy).name())); - - init_test(); - - Dog dog; - virtual_ptr p(dog); - - BOOST_TEST_MESSAGE("After first call to initialize:"); - BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); - BOOST_TEST_MESSAGE( - "static_vptr = " << Policy::template static_vptr); - BOOST_TEST(p.vptr() == Policy::template static_vptr); - - // 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(); - - BOOST_TEST_MESSAGE("After second call to initialize:"); - BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); - BOOST_TEST_MESSAGE( - "static_vptr = " << Policy::template static_vptr); - - if constexpr (Policy::template has_facet) { - BOOST_TEST(p.vptr() == Policy::template static_vptr); - } else { - BOOST_TEST(p.vptr() != Policy::template static_vptr); - } -} - -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(&ev)) { - static_assert(std::is_same_v< - decltype(error), const type_mismatch_error*>); - throw *error; - } - }); - - init_test(); - bool threw = false; - - try { - Dog snoopy; - Animal& animal = snoopy; - virtual_ptr::final(animal); - } catch (const type_mismatch_error& error) { - default_policy::set_error_handler(prev_handler); - BOOST_TEST(error.type == reinterpret_cast(&typeid(Dog))); - threw = true; - } catch (...) { - default_policy::set_error_handler(prev_handler); - BOOST_FAIL("wrong exception"); - return; - } - - if constexpr (default_policy::has_facet) { - if (!threw) { - BOOST_FAIL("should have thrown"); - } - } else { - if (threw) { - BOOST_FAIL("should not have thrown"); - } - } -} diff --git a/test/test_virtual_ptr_value_semantics.cpp b/test/test_virtual_ptr_value_semantics.cpp new file mode 100644 index 0000000..98b15e2 --- /dev/null +++ b/test/test_virtual_ptr_value_semantics.cpp @@ -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 + +#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::element_type, Animal>); + static_assert(std::is_same_v< + decltype(std::declval>().get()), + Animal*>); + static_assert(!virtual_ptr::is_smart_ptr); + static_assert(!virtual_ptr::is_smart_ptr); + static_assert( + std::is_same_v< + decltype(*std::declval>()), Animal&>); + + init_test(); + + // ------------------------------------------------------------------------- + // construction and assignment from plain references and pointers + + { + virtual_ptr p{nullptr}; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + { + Dog snoopy; + virtual_ptr p(snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + Dog hector; + p = hector; + BOOST_TEST(p.get() == &hector); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + Dog snoopy; + virtual_ptr p(snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + Cat felix; + p = felix; + BOOST_TEST(p.get() == &felix); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + const Dog snoopy; + virtual_ptr p(snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + const Dog hector; + p = hector; + BOOST_TEST(p.get() == &hector); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + const Dog snoopy; + virtual_ptr p(snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + const Cat felix; + p = felix; + BOOST_TEST(p.get() == &felix); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + Dog snoopy; + virtual_ptr p(&snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + Dog hector; + p = &hector; + BOOST_TEST(p.get() == &hector); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + Dog snoopy; + virtual_ptr p(&snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + Cat felix; + p = &felix; + BOOST_TEST(p.get() == &felix); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + const Dog snoopy; + virtual_ptr p(&snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + const Dog hector; + p = &hector; + BOOST_TEST(p.get() == &hector); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + const Dog snoopy; + virtual_ptr p(&snoopy); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + const Cat felix; + p = &felix; + BOOST_TEST(p.get() == &felix); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + // virtual_ptr p{Dog()}; + static_assert(!construct_assign_ok, Dog&&>); + + // ------------------------------------------------------------------------- + // construction and assignment from other virtual_ptr + + { + // virtual_ptr(const virtual_ptr&) + Dog snoopy; + const virtual_ptr p(snoopy); + virtual_ptr q(p); + BOOST_TEST(q.get() == &snoopy); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr(virtual_ptr&) + Dog snoopy; + virtual_ptr p(snoopy); + virtual_ptr q(p); + BOOST_TEST(q.get() == &snoopy); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr(virtual_ptr&&) + Dog snoopy; + virtual_ptr p(snoopy); + virtual_ptr q(std::move(p)); + BOOST_TEST(q.get() == &snoopy); + BOOST_TEST(q.vptr() == Policy::template static_vptr); + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr(const virtual_ptr&) + Dog snoopy; + const virtual_ptr p(snoopy); + virtual_ptr base(p); + BOOST_TEST(base.get() == &snoopy); + BOOST_TEST(base.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr(const virtual_ptr&) + Dog snoopy; + const virtual_ptr p(snoopy); + virtual_ptr const_q(p); + BOOST_TEST(const_q.get() == &snoopy); + BOOST_TEST(const_q.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr(const virtual_ptr&) + Dog snoopy; + const virtual_ptr p(snoopy); + virtual_ptr const_base_q(p); + BOOST_TEST(const_base_q.get() == &snoopy); + BOOST_TEST(const_base_q.vptr() == Policy::template static_vptr); + } + + { + // virtual_ptr() + virtual_ptr p{nullptr}; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + // ------------------------------------------------------------------------- + // assignment + + { + virtual_ptr p; + Dog snoopy; + p = snoopy; + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + virtual_ptr p; + Dog snoopy; + p = &snoopy; + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + virtual_ptr p; + Dog snoopy; + p = snoopy; + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + virtual_ptr p; + Dog snoopy; + p = &snoopy; + BOOST_TEST(p.get() == &snoopy); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } + + { + Dog snoopy; + virtual_ptr p(snoopy); + p = nullptr; + BOOST_TEST(p.get() == nullptr); + BOOST_TEST(p.vptr() == nullptr); + } + + static_assert( + !construct_assign_ok, const Dog&>); + static_assert( + !construct_assign_ok, 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(); + + Dog snoopy; + virtual_ptr p(snoopy); + + BOOST_TEST_MESSAGE("After first call to initialize:"); + BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); + BOOST_TEST_MESSAGE( + "static_vptr = " << Policy::template static_vptr); + BOOST_TEST(p.vptr() == Policy::template static_vptr); + + // 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(); + + BOOST_TEST_MESSAGE("After second call to initialize:"); + BOOST_TEST_MESSAGE("p.vptr() = " << p.vptr()); + BOOST_TEST_MESSAGE( + "static_vptr = " << Policy::template static_vptr); + + if constexpr (Policy::template has_facet) { + BOOST_TEST(p.vptr() == Policy::template static_vptr); + } else { + BOOST_TEST(p.vptr() != Policy::template static_vptr); + } +} + +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(&ev)) { + static_assert(std::is_same_v< + decltype(error), const type_mismatch_error*>); + throw *error; + } + }); + + init_test(); + bool threw = false; + + try { + Dog snoopy; + Animal& animal = snoopy; + virtual_ptr::final(animal); + } catch (const type_mismatch_error& error) { + default_policy::set_error_handler(prev_handler); + BOOST_TEST(error.type == reinterpret_cast(&typeid(Dog))); + threw = true; + } catch (...) { + default_policy::set_error_handler(prev_handler); + BOOST_FAIL("wrong exception"); + return; + } + + if constexpr (default_policy::has_facet) { + if (!threw) { + BOOST_FAIL("should have thrown"); + } + } else { + if (threw) { + BOOST_FAIL("should not have thrown"); + } + } +} diff --git a/test/test_virtual_ptr_value_semantics.hpp b/test/test_virtual_ptr_value_semantics.hpp new file mode 100644 index 0000000..1e148bf --- /dev/null +++ b/test/test_virtual_ptr_value_semantics.hpp @@ -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 +#include +#include +#include + +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 +void init_test() { + BOOST_OPENMETHOD_REGISTER(use_classes); + struct id; + (void)&method), void, Policy>::fn; + boost::openmethod::initialize(); +} + +struct direct_vector_policy : default_policy::fork {}; + +struct indirect_vector_policy + : default_policy::fork::replace< + extern_vptr, vptr_vector> {}; + +struct direct_map_policy : default_policy::fork::replace< + extern_vptr, vptr_map> {}; + +struct indirect_map_policy + : default_policy::fork::replace< + extern_vptr, vptr_map> {}; + +using test_policies = boost::mp11::mp_list< + direct_vector_policy, indirect_vector_policy, direct_map_policy, + indirect_map_policy>; + +template< + template class smart_ptr, + template 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, Policy>, smart_ptr>); + + // a virtual_ptr cannot be constructed from const smart_ptr + static_assert( + !std::is_constructible_v< + virtual_ptr, Policy>, smart_ptr>); + + // policies must be the same + static_assert(!std::is_constructible_v< + virtual_ptr, policies::debug>, + virtual_ptr, policies::release>>); + + // a smart virtual_ptr cannot be constructed from a plain reference or + // pointer + static_assert(!std::is_constructible_v< + virtual_ptr, Policy>, Animal&>); + static_assert(!std::is_constructible_v< + virtual_ptr, Policy>, Animal*>); + + static_assert( + !std::is_constructible_v, const other_smart_ptr&>); + // smart_ptr p{other_smart_ptr()}; + + static_assert(!std::is_constructible_v< + virtual_ptr, Policy>, + virtual_ptr>); + + // --------------------- + // test other properties + + static_assert(virtual_ptr, Policy>::is_smart_ptr); + static_assert(virtual_ptr, Policy>::is_smart_ptr); + + static_assert(std::is_same_v< + typename virtual_ptr, Policy>::element_type, + Animal>); + + static_assert( + std::is_same_v< + decltype(std::declval, Policy>>() + .get()), + Animal*>); + + static_assert( + std::is_same_v< + decltype(*std::declval, Policy>>()), + Animal&>); + + static_assert( + std::is_same_v< + decltype(std::declval, Policy>>() + .pointer()), + const smart_ptr&>); + + static_assert( + std::is_same_v< + decltype(*std::declval, Policy>>()), + Animal&>); +}; + +template +constexpr bool construct_assign_ok = + std::is_constructible_v && std::is_assignable_v; + +#endif // TEST_VIRTUAL_PTR_VALUE_SEMANTICS_HPP