// Copyright (c) 2018-2025 Jean-Louis Leroy // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_OPENMETHOD_CORE_HPP #define BOOST_OPENMETHOD_CORE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_OPENMETHOD_DEFAULT_REGISTRY #define BOOST_OPENMETHOD_DEFAULT_REGISTRY ::boost::openmethod::default_registry #endif #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4100) #pragma warning(disable : 4646) #pragma warning(disable : 4702) // unreachable code #endif //! Top namespace of the library. namespace boost::openmethod { #ifdef __MRDOCS__ #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS #define BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS #else #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS namespace detail { #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS } #define BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS detail:: #endif namespace detail { using sfinae = void; } template< class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY, typename = detail::sfinae> class virtual_ptr; // ============================================================================= // Helpers namespace detail { using macro_default_registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY; template struct extract_registry; template<> struct extract_registry<> { using registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY; using others = mp11::mp_list<>; }; template struct extract_registry { using registry = std::conditional_t< is_registry, Type, BOOST_OPENMETHOD_DEFAULT_REGISTRY>; using others = std::conditional_t< is_registry, mp11::mp_list<>, mp11::mp_list>; }; template struct extract_registry { static_assert(!is_registry, "policy must be the last in the list"); using registry = typename extract_registry::registry; using others = mp11::mp_push_front< typename extract_registry::others, Type1>; }; template struct init_type_ids; template struct init_type_ids> { static auto fn(type_id* ids) { (..., (*ids++ = Registry::rtti::template static_type())); return ids; } }; template struct is_unambiguous_accessible_base_of : std::is_base_of { static_assert( std::is_base_of_v == std::is_convertible_v, "class must be an accessible unambiguous base, repeated inheritance is " "not " "supported"); }; // Collect the base classes of a list of classes. The result is a mp11 map that // associates each class to a list starting with the class itself, followed by // all its bases, as per std::is_base_of. Thus the list includes the class // itself at least twice: at the front, and down the list, as its own improper // base. The direct and indirect bases are all included. The runtime will // extract the direct proper bases. template using inheritance_map = mp11::mp_list, mp11::mp_list>, Cs>...>; // ============================================================================= // optimal_cast template struct requires_dynamic_cast_ref_aux : std::true_type {}; template struct requires_dynamic_cast_ref_aux< B, D, std::void_t(std::declval()))>> : std::false_type {}; template constexpr bool requires_dynamic_cast = detail::requires_dynamic_cast_ref_aux::value; template auto optimal_cast(B&& obj) -> decltype(auto) { if constexpr (requires_dynamic_cast) { return Registry::rtti::template dynamic_cast_ref( std::forward(obj)); } else { return static_cast(obj); } } // ============================================================================= // Common details template struct is_virtual : std::false_type {}; template struct is_virtual> : std::true_type {}; template struct remove_virtual_aux { using type = T; }; template struct remove_virtual_aux> { using type = T; }; template using remove_virtual_ = typename remove_virtual_aux::type; template struct virtual_type_aux { using type = void; }; template struct virtual_type_aux< T, Registry, std::void_t::virtual_type>> { using type = typename virtual_traits::virtual_type; }; template using virtual_type = typename virtual_type_aux::type; template using virtual_types = boost::mp11::mp_transform< remove_virtual_, boost::mp11::mp_filter>; } // namespace detail BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS //! Removes the virtual_<> decorator, if present (exposition only). //! //! Provides a nested `type` equal to `T`. The template is specialized for //! `virtual_`. //! //! @tparam T A type. template struct StripVirtualDecorator { //! Same as `T` using type = T; }; //! Removes the virtual_<> decorator (exposition only). //! //! Provides a nested `type` equal to `T`. //! //! @tparam T A type. template struct StripVirtualDecorator> { //! Same as `T`. using type = T; }; BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS // ============================================================================= // virtual_traits //! Traits for types used as virtual parameters. //! //! `virtual_traits` must be specialized for each type that can be used as a //! virtual parameters. It enables methods to: //! @li find the type of the object the argument refers to (e.g. `Node` from //! `Node&`) //! @li obtain a non-modifiable reference to that object (e.g. a `const Node&` from //! `Node&`) //! @li cast the argument to another type (e.g. cast a `Node&` to a `Plus&`) //! //! @par Requirements //! //! Specializations of `virtual_traits` must provide the members described to //! the @ref VirtualTraits blueprint. //! //! @tparam T A type referring (in the broad sense) to an instance of a class. //! @tparam Registry A @ref registry. template struct virtual_traits; //! Specialize virtual_traits for lvalue reference types. //! //! @tparam Class A class type, possibly cv-qualified. //! @tparam Registry A @ref registry. template struct virtual_traits { //! `Class`, stripped from cv-qualifiers. using virtual_type = std::remove_cv_t; //! Return a reference to a non-modifiable `Class` object. //! @param arg A reference to a non-modifiable `Class` object. //! @return A reference to the same object. static auto peek(const Class& arg) -> const Class& { return arg; } //! Cast to another type. //! //! Cast an object to another type. If possible, use `static_cast`. //! Otherwise, use `Registry::rtti::dynamic_cast_ref`. //! //! @tparam Derived A lvalue reference type. //! @param obj A reference to a `Class` object. //! @return A reference to the same object, cast to `Derived`. template static auto cast(Class& obj) -> Derived { static_assert(std::is_lvalue_reference_v); return detail::optimal_cast(obj); } }; //! Specialize virtual_traits for xvalue reference types. //! //! @tparam T A xvalue reference type. //! @tparam Registry A @ref registry. template struct virtual_traits { //! Same as `Class`. using virtual_type = Class; //! Return a reference to a non-modifiable `Class` object. //! @param arg A reference to a non-modifiable `Class` object. //! @return A reference to the same object. static auto peek(const Class& arg) -> const Class& { return arg; } //! Cast to another type. //! //! Cast an object to another type. If possible, use `static_cast`. //! Otherwise, use `Registry::rtti::dynamic_cast_ref`. //! //! @tparam Derived A rvalue reference type. //! @param obj A reference to a `Class` object. //! @return A reference to the same object, cast to `Derived`. template static auto cast(Class&& obj) -> Derived { static_assert(std::is_rvalue_reference_v); return detail::optimal_cast(obj); } }; //! Specialize virtual_traits for pointer types. //! //! @tparam Class A class type, possibly cv-qualified. //! @tparam Registry A @ref registry. template struct virtual_traits { //! `Class`, stripped from cv-qualifiers. using virtual_type = std::remove_cv_t; //! Return a reference to a non-modifiable `Class` object. //! @param arg A pointer to a non-modifiable `Class` object. //! @return A const reference to the same object. static auto peek(const Class* arg) -> const Class& { return *arg; } //! Cast to another type. //! //! Cast an object to another type. If possible, use `static_cast`. //! Otherwise, use `Registry::rtti::dynamic_cast_ref`. //! //! @tparam Derived A pointer type. //! @param obj A pointer to a `Class` object. //! @return A pointer to the same object, cast to `Derived`. template static auto cast(Class* ptr) -> Derived { static_assert(std::is_pointer_v); if constexpr (detail::requires_dynamic_cast) { return dynamic_cast(ptr); } else { return static_cast(ptr); } } }; namespace detail { template struct use_class_aux; template struct use_class_aux> : std::conditional_t< Registry::has_deferred_static_rtti, detail::deferred_class_info, detail::class_info> { static type_id bases[sizeof...(Bases)]; use_class_aux() { this->first_base = bases; this->last_base = bases + sizeof...(Bases); this->is_abstract = std::is_abstract_v; this->static_vptr = &Registry::template static_vptr; if constexpr (!Registry::has_deferred_static_rtti) { resolve_type_ids(); } Registry::classes.push_back(*this); } void resolve_type_ids() { this->type = Registry::rtti::template static_type(); auto iter = bases; (..., (*iter++ = Registry::rtti::template static_type())); } ~use_class_aux() { Registry::classes.remove(*this); } }; template type_id use_class_aux< Registry, mp11::mp_list>::bases[sizeof...(Bases)]; template using use_classes_tuple_type = boost::mp11::mp_apply< std::tuple, boost::mp11::mp_transform_q< boost::mp11::mp_bind_front< detail::use_class_aux, typename detail::extract_registry::registry>, boost::mp11::mp_apply< detail::inheritance_map, typename detail::extract_registry::others>>>; } // namespace detail //! Add classes to a registry //! //! `use_classes` is a registrar class that adds one or more classes to a //! registry. //! //! Classes potentially involved in a method definition, an overrider, or a //! method call must be registered via `use_classes`. A class may be registered //! multiple times. A class and its direct bases must be listed together in one //! or more instantiations of `use_classes`. //! //! If a class is identified by different type ids in different translation //! units, it must be registered in as many translation units as necessary for //! `use_classes` to register all the type ids. This situation can occur when //! using standard RTTI, because the address of the `type_info` objects are used //! as type ids, and the standard does not guarantee that there is exactly one //! such object per class. The only such case known to the author is when using //! Windows DLLs. //! //! Virtual and multiple inheritance are supported, with the exclusion of //! repeated inheritance. template class use_classes { detail::use_classes_tuple_type tuple; }; // ============================================================================= // virtual_ptr namespace detail { void boost_openmethod_vptr(...); template struct is_smart_ptr_aux : std::false_type {}; template struct is_smart_ptr_aux< Class, Registry, std::void_t< typename virtual_traits::template rebind>> : std::true_type {}; template struct same_smart_ptr_aux : std::false_type {}; template struct same_smart_ptr_aux< Class, Other, Registry, std::void_t::template rebind< typename Other::element_type>>> : std::is_same< Other, typename virtual_traits::template rebind< typename Other::element_type>> {}; } // namespace detail BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS //! Test if argument is polymorphic (exposition only) //! //! Evaluates to `true` if `Class` is a polymorphic type, according to the //! `rtti` policy of `Registry`. //! //! If Registry's `rtti` policy is std_rtti`, this is the same as //! `std::is_polymorphic`. However, other `rtti` policies may have a different //! view of what is polymorphic. //! //! @tparam Class A class type. //! @tparam Registry A registry. template constexpr bool IsPolymorphic = Registry::rtti::template is_polymorphic; //! Test if argument is a smart pointer (exposition only) //! //! Evaluates to `true` if `Class` is a smart pointer type, and false otherwise. //! `Class` is considered a smart pointer if `virtual_traits` //! exists and it defines a nested template `rebind` that can be instantiated //! with `Class`. //! //! @tparam Class A class type. //! @tparam Registry A registry. template constexpr bool IsSmartPtr = detail::is_smart_ptr_aux::value; //! Test if arguments are same kind of smart pointers (exposition only) //! //! Evaluates to `true` if `Class` and `Other` are both smart pointers of the //! same type. //! //! @tparam Class A class type. //! @tparam Other Another class type. //! @tparam Registry A registry. template constexpr bool SameSmartPtr = detail::same_smart_ptr_aux::value; BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS template inline auto final_virtual_ptr(Arg&& obj); namespace detail { template struct is_virtual> : std::true_type {}; template struct is_virtual&> : std::true_type {}; template struct is_virtual&> : std::true_type { }; template struct is_virtual_ptr_aux : std::false_type {}; template struct is_virtual_ptr_aux> : std::true_type { }; template struct is_virtual_ptr_aux&> : std::true_type {}; template constexpr bool is_virtual_ptr = detail::is_virtual_ptr_aux::value; template constexpr bool has_vptr_fn = std::is_same_v< decltype(boost_openmethod_vptr( std::declval(), std::declval())), vptr_type>; template decltype(auto) acquire_vptr(const ArgType& arg) { Registry::require_initialized(); if constexpr (detail::has_vptr_fn) { return boost_openmethod_vptr(arg, static_cast(nullptr)); } else { return Registry::template policy::dynamic_vptr(arg); } } 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; } // namespace detail //! Creates a `virtual_ptr` for an object of a known dynamic type. //! //! Creates a @ref virtual_ptr to an object, setting its v-table pointer //! according to the declared type of its argument. Assumes that the static and //! dynamic types are the same. Sets the v-table pointer to the //! @ref registry::static_vptr for the class. //! //! `Class` is _not_ required to be polymorphic. //! //! If runtime checks are enabled, and the argument is polymorphic, checks if //! the static and dynamic types are the same. If not, calls the error handler //! with a @ref final_error value, then terminates the program with @ref abort. //! //! @par Errors //! //! @li @ref final_error The static and dynamic types of the object are //! different. //! //! @tparam Registry A @ref registry. //! @tparam Arg The type of the argument. //! @param obj A reference to an object. //! @return A `virtual_ptr` pointing to `obj`. template inline auto final_virtual_ptr(Arg&& obj) { using namespace detail; using VirtualPtr = virtual_ptr, Registry>; using Traits = virtual_traits; using Class = typename Traits::virtual_type; static_assert(!std::is_const_v); static_assert(!std::is_volatile_v); static_assert(!std::is_reference_v); static_assert(!std::is_pointer_v); Registry::require_initialized(); if constexpr ( Registry::has_runtime_checks && Registry::rtti::template is_polymorphic) { // check that dynamic type == static type auto static_type = Registry::rtti::template static_type(); auto dynamic_type = Registry::rtti::dynamic_type(Traits::peek(obj)); if (dynamic_type != static_type) { if constexpr (is_not_void) { final_error error; error.static_type = static_type; error.dynamic_type = dynamic_type; Registry::error_handler::error(error); } abort(); } } const vptr_type& vptr = Registry::template static_vptr; BOOST_ASSERT(vptr); return VirtualPtr( std::forward(obj), detail::box_vptr(vptr)); } //! Create a `virtual_ptr` for an object of a known dynamic type. //! //! This is an overload of `final_virtual_ptr` that uses the default //! registry as the `Registry` template parameter. //! //! @see @ref final_virtual_ptr // We could give a default value to Registry in the main template, but gcc // doesn't like it. template inline auto final_virtual_ptr(Arg&& obj) { return final_virtual_ptr( std::forward(obj)); } //! Wide pointer combining pointers to an object and its v-table //! //! A `virtual_ptr` is a wide pointer that combines pointers to an object and //! its v-table. Calls to methods via `virtual_ptr` are as fast as ordinary //! virtual function calls (typically two instructions). //! //! A `virtual_ptr` can be implicitly constructed from a reference, a pointer, //! or another `virtual_ptr`, provided that they are type-compatible. //! //! `virtual_ptr` has specializations that use a `std::shared_ptr` or a //! `std::unique_ptr` as the pointer to the object. The mechanism can be //! extended to other smart pointers by specializing @ref virtual_traits. A //! "plain" `virtual_ptr` can be constructed from a smart `virtual_ptr`, but not //! the other way around. //! //! The default value for `Registry` can be customized by defining the //! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}} //! preprocessor symbol. //! //! @par Requirements //! //! @li @ref virtual_traits must be specialized for `Class&`. //! @li `Class` must be a class type, possibly cv-qualified, registered in //! `Registry`. //! //! @tparam Class The class of the object, possibly cv-qualified //! @tparam Registry The registry in which `Class` is registered //! @tparam unnamed Implementation defined, use default template class virtual_ptr { using traits = virtual_traits; #ifndef __MRDOCS__ template friend class virtual_ptr; template friend auto final_virtual_ptr(Arg&& obj); #endif static constexpr bool is_smart_ptr = false; static constexpr bool use_indirect_vptrs = Registry::has_indirect_vptr; std::conditional_t vp; Class* obj; template< class Other, typename = std::enable_if_t>> virtual_ptr(Other& other, decltype(vp) vp) : vp(vp), obj(&other) { } public: //! Class //! //! This is the same as `Class`. using element_type = Class; //! Default constructor //! //! @note This constructor does nothing. The state of the two pointers //! inside the object is as specified for uninitialized variables by C++. virtual_ptr() = default; //! Construct from `nullptr` //! //! Set both object and v-table pointers to `nullptr`. //! //! @param value A `nullptr`. //! //! @par Example //! //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr p{nullptr}; //! BOOST_TEST(p.get() == nullptr); //! BOOST_TEST(p.vptr() == nullptr); //! @endcode //! //! @param value A `nullptr`. explicit virtual_ptr(std::nullptr_t) : vp(detail::box_vptr(detail::null_vptr)), obj(nullptr) { } //! Construct a `virtual_ptr` from a reference to an object //! //! The pointer to the v-table is obtained by calling //! @ref boost_openmethod_vptr if a suitable overload exists, or the //! @ref policies::vptr::fn::dynamic_vptr of the registry's //! `vptr` policy otherwise. //! //! @param other A reference to a polymorphic object //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! Dog snoopy; //! Animal& animal = snoopy; //! //! virtual_ptr p = animal; //! //! BOOST_TEST(p.get() == &snoopy); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `Other` must be a polymorphic class, according to `Registry`'s //! `rtti` policy. //! @li `Other\*` must be constructible from `Class\*`. //! //! @par Errors //! //! The following errors may occur, depending on the policies selected in //! `Registry`: //! //! @li @ref missing_class template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic && std::is_constructible_v>> virtual_ptr(Other& other) : vp(detail::box_vptr( detail::acquire_vptr(other))), obj(&other) { } //! Construct a `virtual_ptr` from a pointer to an object //! //! The pointer to the v-table is obtained by calling //! @ref boost_openmethod_vptr if a suitable overload exists, or the //! @ref policies::vptr::fn::dynamic_vptr of the registry's //! `vptr` policy otherwise. //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! Dog snoopy; //! Animal* animal = &snoopy; //! //! virtual_ptr p = animal; //! //! BOOST_TEST(p.get() == &snoopy); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @param other A pointer to a polymorphic object //! //! @par Requirements //! //! @li `Other` must be a polymorphic class, according to `Registry`'s //! `rtti` policy. //! //! @li `Other\*` must be constructible from `Class\*`. //! //! @par Errors //! //! The following errors may occur, depending on the policies selected in //! `Registry`: //! //! @li @ref missing_class template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic && std::is_constructible_v>> virtual_ptr(Other* other) : vp(detail::box_vptr( detail::acquire_vptr(*other))), obj(other) { } //! Construct a `virtual_ptr` from another `virtual_ptr` //! //! Copy the object and v-table pointers from `other` to `this. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Examples //! //! Assigning from a plain virtual_ptr: //! //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! Dog snoopy; //! virtual_ptr dog = final_virtual_ptr(snoopy); //! virtual_ptr p{nullptr}; //! //! p = dog; //! //! BOOST_TEST(p.get() == &snoopy); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! Assigning from a smart virtual_ptr: //! //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr> snoopy = make_shared_virtual(); //! virtual_ptr p = snoopy; //! //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! No construction of a smart `virtual_ptr` from a plain `virtual_ptr`: //! //! @code //! static_assert( //! std::is_constructible_v< //! shared_virtual_ptr, virtual_ptr> == false); //! @endcode //! //! @param other A virtual_ptr to a type-compatible object //! //! @par Requirements //! @li `Other`\'s object pointer must be assignable to a `Class\*`. template< class Other, typename = std::enable_if_t::element_type*>>> virtual_ptr(const virtual_ptr& other) : vp(other.vp), obj(other.get()) { } //! Assign a `virtual_ptr` from a reference to an object //! //! The pointer to the v-table is obtained by calling //! @ref boost_openmethod_vptr if a suitable overload exists, or the //! @ref policies::vptr::fn::dynamic_vptr of the registry's //! `vptr` policy otherwise. //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr p{nullptr}; //! Dog snoopy; //! Animal& animal = snoopy; //! //! p = animal; //! //! BOOST_TEST(p.get() == &snoopy); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @param other A reference to a polymorphic object //! //! @par Requirements //! //! @li `Other` must be a polymorphic class, according to `Registry`'s //! `rtti` policy. //! //! @li `Other\*` must be constructible from `Class\*`. //! //! @par Errors //! //! The following errors may occur, depending on the policies selected in //! `Registry`: //! //! @li @ref missing_class template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic && std::is_assignable_v>> virtual_ptr& operator=(Other& other) { obj = &other; vp = detail::box_vptr( detail::acquire_vptr(other)); return *this; } //! Assign a `virtual_ptr` from a pointer to an object //! //! The pointer to the v-table is obtained by calling //! @ref boost_openmethod_vptr if a suitable overload exists, or the //! @ref policies::vptr::fn::dynamic_vptr of the registry's //! `vptr` policy otherwise. //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr p{nullptr}; //! Dog snoopy; //! Animal* animal = &snoopy; //! //! p = animal; //! //! BOOST_TEST(p.get() == &snoopy); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @param other A pointer to a polymorphic object //! //! @par Requirements //! @li `Other` must be a polymorphic class, according to `Registry`'s //! `rtti` policy. //! @li `Other\*` must be constructible from `Class\*`. //! //! @par Errors //! //! The following errors may occur, depending on the policies selected in //! `Registry`: //! //! @li @ref missing_class template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic && std::is_assignable_v>> virtual_ptr& operator=(Other* other) { obj = other; vp = detail::box_vptr( detail::acquire_vptr(*other)); return *this; } //! Assign a `virtual_ptr` from another `virtual_ptr` //! //! Copy the object and v-table pointers from `other` to `this. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Examples //! //! Assigning from a plain virtual_ptr: //! //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! Dog snoopy; //! virtual_ptr dog = final_virtual_ptr(snoopy); //! virtual_ptr p{nullptr}; //! //! p = dog; //! //! BOOST_TEST(p.get() == &snoopy); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! Assigning from a smart virtual_ptr: //! //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr> snoopy = make_shared_virtual(); //! virtual_ptr p; //! //! p = snoopy; //! //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! No assignment from a plain `virtual_ptr` to a smart `virtual_ptr`: //! //! @code //! static_assert( //! std::is_assignable_v< //! shared_virtual_ptr&, virtual_ptr> == false); //! @endcode //! //! @param other A virtual_ptr to a type-compatible object //! //! @par Requirements //! @li `Other`\'s object pointer must be assignable to a `Class\*`. template< class Other, typename = std::enable_if_t::element_type*>>> virtual_ptr& operator=(const virtual_ptr& other) { obj = other.get(); vp = other.vp; return *this; } //! Set a `virtual_ptr` to `nullptr` //! //! Set both object and v-table pointers to `nullptr`. //! //! @par Example //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! Dog snoopy; //! virtual_ptr p = final_virtual_ptr(snoopy); //! //! p = nullptr; //! //! BOOST_TEST(p.get() == nullptr); //! BOOST_TEST(p.vptr() == nullptr); //! //! @code //! @endcode virtual_ptr& operator=(std::nullptr_t) { obj = nullptr; vp = detail::box_vptr(detail::null_vptr); return *this; } //! Get a pointer to the object //! //! @return A pointer to the object auto get() const -> Class* { return obj; } //! Get a pointer to the object //! //! @return A pointer to the object auto operator->() const { return get(); } //! Get a reference to the object //! //! @return A reference to the object auto operator*() const -> element_type& { return *get(); } //! Get a pointer to the object //! //! @return A pointer to the object auto pointer() const -> element_type* { return obj; } //! Cast to another `virtual_ptr` type //! //! @par Example //! @code //! @endcode //! //! @tparam Other The target class of the cast //! @return A `virtual_ptr` pointing to the same object //! @par Requirements //! @li `Other` must be a base or derived class of `Class`. template< class Other, typename = std::enable_if_t< std::is_base_of_v || std::is_base_of_v>> auto cast() const -> decltype(auto) { return virtual_ptr( traits::template cast(*obj), vp); } //! Construct a `virtual_ptr` from a reference to an object //! //! This function forwards to @ref final_virtual_ptr. //! //! @tparam Other The type of the argument //! @param obj A reference to an object //! @return A `virtual_ptr` pointing to `obj` template static auto final(Other&& obj) { return final_virtual_ptr(std::forward(obj)); } //! Get the v-table pointer //! @return The v-table pointer auto vptr() const { return detail::unbox_vptr(this->vp); } }; //! Wide pointer combining a smart pointer to an object and a pointer to its //! v-table //! //! This specialization of `virtual_ptr` uses a smart pointer to track the //! object, instead of a plain pointer. //! //! @tparam SmartPtr A smart pointer type //! @tparam Registry The registry in which the underlying class is registered template class virtual_ptr< SmartPtr, Registry, std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsSmartPtr>> { #ifndef __MRDOCS__ template friend class virtual_ptr; template friend auto final_virtual_ptr(Arg&& obj); #endif static constexpr bool is_smart_ptr = true; static constexpr bool use_indirect_vptrs = Registry::has_indirect_vptr; using traits = virtual_traits; std::conditional_t vp; SmartPtr obj; template< class Other, typename = std::enable_if_t>> virtual_ptr(Other& other, decltype(vp) vp) : vp(vp), obj(&other) { } template virtual_ptr(Arg&& obj, decltype(vp) vp) : vp(vp), obj(std::forward(obj)) { } public: //! Class pointed to by SmartPtr using element_type = typename SmartPtr::element_type; //! Default constructor //! //! Construct the object pointer using its default constructor. Set the //! v-table pointer to `nullptr`. //! //! @par Example //! @code //! struct Dog {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Dog); //! initialize(); //! //! virtual_ptr> p; //! BOOST_TEST(p.get() == nullptr); //! BOOST_TEST(p.vptr() == nullptr); //! @par Example //! @endcode virtual_ptr() : vp(detail::box_vptr(detail::null_vptr)) { } //! Construct from `nullptr` //! //! Construct the object pointer using its default constructor. Set the //! v-table pointer to `nullptr`. //! //! @par Example //! @code //! struct Dog {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Dog); //! initialize(); //! //! virtual_ptr> p{nullptr}; //! BOOST_TEST(p.get() == nullptr); //! BOOST_TEST(p.vptr() == nullptr); //! @endcode //! //! @param value A `nullptr`. explicit virtual_ptr(std::nullptr_t) : vp(detail::box_vptr(detail::null_vptr)) { } virtual_ptr(const virtual_ptr& other) = default; virtual_ptr(virtual_ptr&& other) : vp(std::exchange( other.vp, detail::box_vptr(detail::null_vptr))), obj(std::move(other.obj)) { } #ifdef __MRDOCS__ //! Construct from a (const) smart pointer to a derived class //! //! Set the object pointer with a copy of `other`. Set the v-table pointer //! according to the dynamic type of `*other`. //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! const std::shared_ptr snoopy = std::make_shared(); //! virtual_ptr> p = snoopy; //! //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a polymorphic class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `const Other&`. template< class Other, typename = std::enable_if_t< SameSmartPtr && IsPolymorphic && std::is_constructible_v>> #else template< class Other, typename = std::enable_if_t< detail::SameSmartPtr && detail::IsPolymorphic && std::is_constructible_v>> #endif virtual_ptr(const Other& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) : detail::null_vptr)), obj(other) { } #if __MRDOCS__ //! Construct from a smart pointer to a derived class //! //! Copy object pointer from `other` to `this`. Set the v-table pointer //! according to the dynamic type of `*other`. //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! std::shared_ptr snoopy = std::make_shared(); //! virtual_ptr> p = snoopy; //! //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a polymorphic class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&`. template< class Other, typename = std::enable_if_t< SameSmartPtr && IsPolymorphic && std::is_constructible_v>> #else template< class Other, typename = std::enable_if_t< detail::SameSmartPtr && detail::IsPolymorphic && std::is_constructible_v>> #endif virtual_ptr(Other& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) : detail::null_vptr)), obj(other) { } #ifdef __MRDOCS__ //! Move-construct from a smart pointer to a derived class //! //! Move object pointer from `other` to `this`. Set the v-table pointer //! according to the dynamic type of `*other`. //! //! @par Example //! @code //! struct Animal { virtual ~Animal() { } }; // polymorphic //! struct Dog : Animal {}; // polymorphic //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! std::shared_ptr snoopy = std::make_shared(); //! Dog* moving = snoopy.get(); //! //! virtual_ptr> p = std::move(snoopy); //! //! BOOST_TEST(p.get() == moving); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.get() == nullptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a polymorphic class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&&`. template< class Other, typename = std::enable_if_t< SameSmartPtr && IsPolymorphic && std::is_constructible_v>> #else template< class Other, typename = std::enable_if_t< detail::SameSmartPtr && detail::IsPolymorphic && std::is_constructible_v>> #endif virtual_ptr(Other&& other) : vp(detail::box_vptr( other ? detail::acquire_vptr(*other) : detail::null_vptr)), obj(std::move(other)) { } //! Construct from a smart virtual (const) pointer to a derived class //! //! Copy the object and v-table pointers from `other`. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Example //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! const virtual_ptr> snoopy = make_shared_virtual(); //! virtual_ptr> p = snoopy; //! //! BOOST_TEST(snoopy.get() != nullptr); //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a virtual pointer to a class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_constructible_v>> virtual_ptr(const virtual_ptr& other) : vp(other.vp), obj(other.obj) { } //! Construct-move from a virtual pointer to a derived class //! //! Move the object pointer from `other` to `this`. Copy the v-table pointer //! from `other`. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Example //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr> snoopy = make_shared_virtual(); //! Dog* dog = snoopy.get(); //! //! virtual_ptr> p = std::move(snoopy); //! //! BOOST_TEST(p.get() == dog); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.get() == nullptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_constructible_v>> virtual_ptr(virtual_ptr&& other) : vp(std::exchange( other.vp, detail::box_vptr(detail::null_vptr))), obj(std::move(other.obj)) { } //! Assign from `nullptr` //! //! Reset the object pointer using its default constructor. Set the //! v-table pointer to `nullptr`. //! //! @par Example //! @code //! struct Dog {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Dog); //! initialize(); //! //! virtual_ptr> p = make_shared_virtual(); //! //! p = nullptr; //! //! BOOST_TEST(p.get() == nullptr); //! BOOST_TEST(p.vptr() == nullptr); //! BOOST_TEST((p == virtual_ptr>())); //! @endcode //! //! @param value A `nullptr`. virtual_ptr& operator=(std::nullptr_t) { obj = SmartPtr(); vp = detail::box_vptr(detail::null_vptr); return *this; } //! Assign from a (const) smart pointer to a derived class //! //! Copy the object pointer from `other` to `this`. Set the v-table pointer //! according to the dynamic type of `*other`. //! //! @par Example //! @code //! virtual_ptr> snoopy = make_shared_virtual(); //! virtual_ptr> p; //! //! p = snoopy; //! //! BOOST_TEST(p.get() != nullptr); //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a polymorphic class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `const Other&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_assignable_v && BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic>> virtual_ptr& operator=(const Other& other) { obj = other; vp = detail::box_vptr( detail::acquire_vptr(*other)); return *this; } //! Move-assign from a smart pointer to a derived class //! //! Move object pointer from `other` to `this`. Set the v-table pointer //! according to the dynamic type of `*other`. //! //! @par Example //! @code //! virtual_ptr> snoopy = make_shared_virtual(); //! Dog* moving = snoopy.get(); //! virtual_ptr> p; //! //! p = std::move(snoopy); //! //! BOOST_TEST(p.get() == moving); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.get() == nullptr); //! BOOST_TEST(snoopy.vptr() == nullptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a polymorphic class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_assignable_v && BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsPolymorphic>> virtual_ptr& operator=(Other&& other) { vp = detail::box_vptr( other ? detail::acquire_vptr(*other) : detail::null_vptr); obj = std::move(other); return *this; } //! Assign from a smart virtual pointer to a derived class //! //! Copy the object and v-table pointers from `other` to `this`. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Example //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr> snoopy = make_shared_virtual(); //! virtual_ptr> p; //! //! p = snoopy; //! //! BOOST_TEST(p.get() != nullptr); //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a virtual pointer to a class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_assignable_v>> virtual_ptr& operator=(virtual_ptr& other) { obj = other.obj; vp = other.vp; return *this; } virtual_ptr& operator=(const virtual_ptr& other) = default; //! Assign from a smart virtual const pointer to a derived class //! //! Copy the object and v-table pointers from `other` to `this`. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Example //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! const virtual_ptr> snoopy = make_shared_virtual(); //! virtual_ptr> p; //! //! p = snoopy; //! //! BOOST_TEST(p.get() != nullptr); //! BOOST_TEST(p.get() == snoopy.get()); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.vptr() == default_registry::static_vptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a virtual pointer to a class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_assignable_v>> virtual_ptr& operator=(const virtual_ptr& other) { obj = other.obj; vp = other.vp; return *this; } //! Move from a virtual pointer to a derived class //! //! Move the object pointer from `other` to `this`. Copy the v-table pointer //! from `other`. //! //! `Other` is _not_ required to be a pointer to a polymorphic class. //! //! @par Example //! @code //! struct Animal {}; // polymorphism not required //! struct Dog : Animal {}; // polymorphism not required //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! initialize(); //! //! virtual_ptr> snoopy = //! make_shared_virtual(); //! Dog* moving = snoopy.get(); //! virtual_ptr> p; //! //! p = std::move(snoopy); //! //! BOOST_TEST(p.get() == moving); //! BOOST_TEST(p.vptr() == default_registry::static_vptr); //! BOOST_TEST(snoopy.get() == nullptr); //! BOOST_TEST(snoopy.vptr() == nullptr); //! @endcode //! //! @par Requirements //! @li `SmartPtr` and `Other` must be instantiated from the same template - //! e.g. both `std::shared_ptr` or both `std::unique_ptr`. //! @li `Other` must be a smart pointer to a class derived from //! `element_type`. //! @li `SmartPtr` must be constructible from `Other&&`. template< class Other, typename = std::enable_if_t< BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS SameSmartPtr && std::is_assignable_v>> virtual_ptr& operator=(virtual_ptr&& other) { vp = std::exchange( other.vp, detail::box_vptr(detail::null_vptr)); obj = std::move(other.obj); return *this; } //! Get a pointer to the object //! //! @return A *plain* pointer to the object auto get() const -> element_type* { return obj.get(); } //! Get a pointer to the object //! //! @return A *plain* pointer to the object auto operator->() const -> element_type* { return get(); } //! Get a reference to the object //! //! @return A reference to the object auto operator*() const -> element_type& { return *get(); } //! Get a smart pointer to the object //! //! @return A const reference to the object pointer auto pointer() const -> const SmartPtr& { return obj; } //! Cast to another `virtual_ptr` type //! @tparam Other The target class of the cast //! @return A `virtual_ptr` pointing to the same object //! @par Requirements //! @li `Other` must be a base or a derived class of `Class`. template< class Other, typename = std::enable_if_t< std::is_base_of_v || std::is_base_of_v>> auto cast() & -> decltype(auto) { using other_smart_ptr = typename traits::template rebind; return virtual_ptr( traits::template cast(obj), vp); } template< class Other, typename = std::enable_if_t< std::is_base_of_v || std::is_base_of_v>> auto cast() const& -> decltype(auto) { using other_smart_ptr = typename traits::template rebind; return virtual_ptr( traits::template cast(obj), vp); } template auto cast() && -> decltype(auto) { static_assert( std::is_base_of_v || std::is_base_of_v); using other_smart_ptr = typename traits::template rebind; return virtual_ptr( traits::template cast(std::move(obj)), vp); } //! Construct a `virtual_ptr` from a smart pointer to an object //! //! This function forwards to @ref final_virtual_ptr. //! //! @tparam Other The type of the argument //! @param obj A reference to an object //! @return A `virtual_ptr` pointing to `obj` template static auto final(Other&& obj) { return final_virtual_ptr(std::forward(obj)); } //! Get the v-table pointer //! @return The v-table pointer auto vptr() const { return detail::unbox_vptr(this->vp); } }; //! Construct a `virtual_ptr` from a lvalue reference. //! //! @tparam Class A class type, possibly cv-qualified. //! @param obj A lvalue reference to an object. //! @return A `virtual_ptr`. template virtual_ptr(Class& obj) -> virtual_ptr; //! Construct a `virtual_ptr` from a xvalue reference. //! //! @tparam Class A class type. //! @param obj A xvalue reference to an object. //! @return A `virtual_ptr`. template virtual_ptr(Class&& obj) -> virtual_ptr; // Alas this is not allowed: // template // virtual_ptr(Class&) -> virtual_ptr; //! Compare two `virtual_ptr`s for equality. //! //! Compare the underlying object pointers for equality. The v-table pointers //! are not compared. //!! //! @tparam Left The type of the left-hand side argument. //! @tparam Right The type of the right-hand side argument. //! @tparam Registry A @ref registry. //! @param left A reference to a `virtual_ptr`. //! @param right A reference to a `virtual_ptr`. //! @return `true` if both `virtual_ptr`s point to the same object or both //! are `nullptr`, `false` otherwise. template auto operator==( const virtual_ptr& left, const virtual_ptr& right) -> bool { return left.pointer() == right.pointer(); } //! Compare two `virtual_ptr`s for inequality. //! //! Compare the underlying object pointers for inequality. The v-table pointers //! are not compared. //!! @tparam Left The type of the left-hand side argument. //! @tparam Right The type of the right-hand side argument. //! @tparam Registry A @ref registry. //! @param left A reference to a `virtual_ptr`. //! @param right A reference to a `virtual_ptr`. //! @return `true` if both `virtual_ptr`s point to different objects, or one //! is `nullptr` and the other is not, `false` otherwise. template auto operator!=( const virtual_ptr& left, const virtual_ptr& right) -> bool { return !(left == right); } //! Specialize virtual_traits for `virtual_ptr`. //! //! Specialize virtual_traits for `virtual_ptr`\'s passed by value. //! //! @tparam Class A class type, possibly cv-qualified. //! @tparam Registry A @ref registry. template struct virtual_traits, Registry> { //! `Class`, stripped from cv-qualifiers. using virtual_type = std::remove_cv_t::element_type>; //! Return a reference to a non-modifiable `Class` object. //! @param arg A reference to a non-modifiable `Class` object. //! @return A reference to the same object. static auto peek(const virtual_ptr& ptr) -> const virtual_ptr& { return ptr; } //! Cast to another type. //! //! Cast a `virtual_ptr` to another type, using its `cast` member function. //! //! @param obj A lvalue reference to a `virtual_ptr`. //! @return A lvalue reference to a `virtual_ptr` to the same object, cast //! to `Derived::element_type`. template static auto cast(const virtual_ptr& ptr) -> decltype(auto) { return ptr.template cast(); } //! Cast to another type. //! //! Cast a `virtual_ptr` to another type, using its `cast` member function. //! //! @param obj A xvalue reference to a `virtual_ptr`. //! @return A xvalue reference to a `virtual_ptr` to the same object, cast //! to `Derived::element_type`. template static auto cast(virtual_ptr&& ptr) -> decltype(auto) { return std::move(ptr).template cast(); } }; //! Specialize virtual_traits for `virtual_ptr`. //! //! Specialize virtual_traits for `virtual_ptr`\'s passed by const reference. //! //! @tparam Class A class type, possibly cv-qualified. //! @tparam Registry A @ref registry. template struct virtual_traits&, Registry> { //! `Class`, stripped from cv-qualifiers. using virtual_type = std::remove_cv_t::element_type>; //! Return a reference to a non-modifiable `Class` object. //! @param arg A reference to a non-modifiable `Class` object. //! @return A reference to the same object. static auto peek(const virtual_ptr& ptr) -> const virtual_ptr& { return ptr; } //! Cast to another type. //! //! Cast a `virtual_ptr` to another type, using its `cast` member function. //! //! @param obj A lvalue reference to a `virtual_ptr`. //! @return A lvalue reference to a `virtual_ptr` to the same object, cast //! to `Derived::element_type`. template static auto cast(const virtual_ptr& ptr) -> decltype(auto) { return ptr.template cast< typename std::remove_reference_t::element_type>(); } }; // ============================================================================= // Method namespace detail { template struct select_overrider_virtual_type_aux { using type = void; }; template struct select_overrider_virtual_type_aux, Q, Registry> { using type = virtual_type; }; template struct select_overrider_virtual_type_aux< virtual_ptr, virtual_ptr, Registry> { using type = typename virtual_traits< virtual_ptr, Registry>::virtual_type; }; template struct select_overrider_virtual_type_aux< const virtual_ptr&, const virtual_ptr&, Registry> { using type = typename virtual_traits< const virtual_ptr&, Registry>::virtual_type; }; template using select_overrider_virtual_type = typename select_overrider_virtual_type_aux::type; template< typename MethodParameters, typename OverriderParameters, class Registry> using overrider_virtual_types = boost::mp11::mp_remove< boost::mp11::mp_transform_q< boost::mp11::mp_bind_back, MethodParameters, OverriderParameters>, void>; template struct init_bad_call { template static auto fn(bad_call& error, const Arg& arg, const Args&... args) { if constexpr (Index == 0u) { error.method = Rtti::template static_type(); error.arity = sizeof...(args) + 1; } type_id arg_type_id; if constexpr (is_virtual_ptr) { arg_type_id = Rtti::dynamic_type(*arg); } else { arg_type_id = Rtti::dynamic_type(arg); } error.types[Index] = arg_type_id; init_bad_call::fn(error, args...); } static auto fn(bad_call&) { } }; template struct init_bad_call { static auto fn(bad_call&) { } }; template using method_base = std::conditional_t< Registry::has_deferred_static_rtti, deferred_method_info, method_info>; template struct parameter_traits { static auto peek(const T& value) -> const T& { return value; } template static auto cast(T value) -> T { return value; } }; template struct parameter_traits, Registry> : virtual_traits {}; template struct parameter_traits, Registry> : virtual_traits, Registry> {}; template struct parameter_traits&, Registry> : virtual_traits&, Registry> {}; template constexpr bool false_t = false; // workaround before CWG2518/P2593R1 template struct validate_method_parameter : std::true_type {}; template struct validate_method_parameter, Registry, U> : std::false_type { static_assert(false_t, "virtual_traits not specialized for type"); }; template struct validate_method_parameter< virtual_, Registry, std::void_t::virtual_type>> : std::bool_constant< has_vptr_fn, Registry> || Registry::rtti::template is_polymorphic>> { static_assert( validate_method_parameter::value, "virtual_<> parameter is not a polymorphic class and no " "boost_openmethod_vptr is applicable"); }; template struct validate_method_parameter, Registry, void> : std::true_type {}; template struct validate_method_parameter< virtual_ptr, MethodRegistry, void> : std::false_type { static_assert( false_t, "registry mismatch"); }; } // namespace detail //! Implement a method //! //! Methods are created by specializing the `method` class template with an //! identifier, a function type and optionally a registry. //! //! `Id` is a type, typically an incomplete class declaration named after the //! method's purpose. It is used to allow different methods with the same //! signature. //! //! `Fn` is a function type, i.e. a type in the form `ReturnType(Parameters...)`. //! //! `Registry` is an instantiation of class template @ref registry. Methods may //! use only classes that have been registered in the same registry as virtual //! parameters and arguments. The registry also contains a set of policies that //! influence several aspects of the dispatch mechanism - for example, how to //! acquire a v-table pointer for an object, how to report errors, whether to //! perform sanity checks, etc. //! //! The default value for `Registry` is @ref default_registry, but it can be //! overridden by defining the preprocessor symbol //! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}, *before* including //! ``. Setting the symbol afterwards has no effect. //! //! Specializations of `method` have a single instance: the static member `fn`, //! which has an `operator()` that forwards to the appropriate overrider. It is //! selected in the same way as overloaded function resolution: //! //! 1. Form the set of all applicable overriders. An overrider is applicable //! if it can be called with the arguments passed to the method. //! //! 2. If the set is empty, call the error handler (if present in the //! registry), then terminate the program with `abort`. //! //! 3. Remove the overriders that are dominated by other overriders in the set. //! Overrider A dominates overrider B if at least one of its virtual formal //! parameters is more specialized than B's, and if none of B's virtual //! parameters is more specialized than A's. //! //! 4. If the resulting set contains exactly one overrider, call it. //! //! If a single most specialized overrider does not exist, the program is //! terminated via `abort`. If the registry contains an @ref error_handler //! policy, its `error` function is called with an object that describes the //! error, prior calling `abort`. `error` may prevent termination by throwing an //! exception. //! //! For each virtual argument `arg`, the dispatch mechanism calls //! `virtual_traits::peek(arg)` and deduces the v-table pointer from the //! `result`, using the first of the following methods that applies: //! //! 1. If `result` is a `virtual_ptr`, get the pointer to the v-table from it. //! //! 2. If `boost_openmethod_vptr` can be called with `result` and a `Registry*`, //! and it returns a `vptr_type`, call it. //! //! 3. Call `Registry::rtti::dynamic_vptr(result)`. //! //! @par N2216 Handling of Ambiguous Calls //! //! If `Registry` was initialized with the @ref N2216 option, ambiguous calls //! are not an error. Instead, the following extra steps are taken to select an //! overrider: //! //! 1. If the return type is a registered polymorphic type, remove all the //! overriders that return a less specific type than others. //! //! 2. If the resulting set contains only one overrider, call it. //! //! 3. Otherwise, call one of the remaining overriders. Which overrider is //! selected is not specified, but it is the same across calls with the //! same arguments types. //! //! @tparam Id A type //! @tparam Fn A function type //! @tparam Registry The registry in which the method is defined template< typename Id, typename Fn, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY> class method; //! Method with a specific id, signature and return type //! //! `method` implements an open-method that takes a parameter list - //! `Parameters` - and returns a `ReturnType`. //! //! `Parameters` must contain at least one virtual parameter, i.e. a parameter //! that has a type in the form `virtual_ptr` or `virtual\_`. //! The dynamic types of the virtual arguments are taken into account to select //! the overrider to call. //! //! @see method //! //! @tparam Id A type representing the method's name //! @tparam ReturnType The return type of the method //! @tparam Parameters The types of the parameters //! @tparam Registry The registry of the method template< typename Id, typename... Parameters, typename ReturnType, class Registry> class method : public detail::method_base { template struct override_aux; // Aliases used in implementation only. Everything extracted from template // arguments is capitalized like the arguments themselves. using RegistryType = Registry; using rtti = typename Registry::rtti; using DeclaredParameters = mp11::mp_list; using CallParameters = boost::mp11::mp_transform; using VirtualParameters = typename detail::virtual_types; using Signature = auto(Parameters...) -> ReturnType; using FunctionPointer = auto (*)(detail::remove_virtual_...) -> ReturnType; public: //! Method singleton //! //! The only instance of `method`. Its `operator()` is used to call //! the method. static method fn; //! Call the method //! //! Call the method with `args`. The types of the arguments are the same as //! the method `Parameters...`, stripped from any `virtual\_` decorators. //! //! @param args The arguments for the method call //! //! @par Errors //! //! If `Registry` contains an @ref error_handler policy, call its `error` //! function with an object of one of the following types: //! //! @li @ref not_implemented: No overrider is applicable. //! @li @ref ambiguous_call: More than one overrider is applicable, and //! none is more specialized than all the others. //! auto operator()(typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS StripVirtualDecorator::type... args) const -> ReturnType; //! Check if a next most specialized overrider exists //! //! Return `true` if a next most specialized overrider after _Fn_ exists, //! and @ref next can be called without causing a @ref bad_call. //! //! @par Requirements //! //! `Fn` must be a function that is an overrider of the method. //! //! @tparam Fn A function that is an overrider of the method. //! @return `true` if a next most specialized overrider exists template static bool has_next(); //! The next most specialized overrider //! //! A pointer to the next most specialized overrider after `Fn`, i.e. the //! overrider that would be called for the same tuple of virtual arguments //! if `Fn` was not present. Set to `nullptr` if no such overrider exists. //! @par Requirements //! //! `Fn` must be a function that is an overrider of the method. //! //! @tparam Fn A function that is an overrider of the method. template static FunctionPointer next; //! Add overriders to method //! //! `override`, instantiated as a static object, adds one or more overriders //! to an open-method. //! //! @par Requirements //! //! `Fn` must be a function that fulfills the following requirements: //! //! @li Have the same number of formal parameters as the method. //! //! @li Each `virtual_ptr` in the method's parameter list must have a //! corresponding `virtual_ptr` parameter in the same position in the //! overrider's parameter list, such that `U` is the same as `T`, or has //! `T` as an accessible unambiguous base. //! //! @li Each `virtual_` in the method's parameter list must have a //! corresponding `U` parameter in the same position in the overrider's //! parameter list, such that `U` is the same as `T`, or has `T` as an //! accessible unambiguous base. //! //! @li All other formal parameters must have the same type as the method's //! corresponding parameters. //! //! @li The return type of the overrider must be the same as the method's //! return type or, if it is a polymorphic type, covariant with the method's //! return type. //! //! @tparam Fn One or more functions to the overrider list template class override { std::tuple...> impl; }; private: static constexpr auto Arity = boost::mp11::mp_count_if< mp11::mp_list, detail::is_virtual>::value; // sanity checks static_assert(( detail::validate_method_parameter::value && ...)); static_assert(Arity > 0, "method has no virtual parameters"); type_id vp_type_ids[Arity]; std::size_t slots_strides[2 * Arity - 1]; // Slots followed by strides. No stride for first virtual argument. // For 1-method: the offset of the method in the method table, which // contains a pointer to a function. // For multi-methods: the offset of the first virtual argument in the // method table, which contains a pointer to the corresponding cell in // the dispatch table, followed by the offset of the second argument and // the stride in the second dimension, etc. void resolve_type_ids(); template auto vptr(const ArgType& arg) const -> vptr_type; template auto resolve_uni(const ArgType& arg, const MoreArgTypes&... more_args) const -> detail::word; template auto resolve_multi_first( const ArgType& arg, const MoreArgTypes&... more_args) const -> detail::word; template< std::size_t VirtualArg, typename MethodArgList, typename ArgType, typename... MoreArgTypes> auto resolve_multi_next( vptr_type dispatch, const ArgType& arg, const MoreArgTypes&... more_args) const -> detail::word; template FunctionPointer resolve(const ArgType&... args) const; template struct thunk; template struct thunk; method(); method(const method&) = delete; method(method&&) = delete; ~method(); void resolve(); // virtual if Registry contains has_deferred_static_rtti static BOOST_NORETURN auto fn_not_implemented( detail::remove_virtual_... args) -> ReturnType; static BOOST_NORETURN auto fn_ambiguous(detail::remove_virtual_... args) -> ReturnType; template< auto Overrider, typename OverriderReturn, typename... OverriderParameters> struct thunk { static auto fn(detail::remove_virtual_... arg) -> ReturnType; using OverriderVirtualParameters = detail::overrider_virtual_types< DeclaredParameters, mp11::mp_list, Registry>; }; template struct override_impl : std::conditional_t< Registry::has_deferred_static_rtti, detail::deferred_overrider_info, detail::overrider_info> { explicit override_impl(FunctionPointer* next = nullptr); void resolve_type_ids(); static type_id vp_type_ids[Arity]; }; template struct override_aux; template struct override_aux { override_aux() { (void)&impl; } static override_impl impl; }; }; template< typename Id, typename... Parameters, typename ReturnType, class Registry> method method::fn; template< typename Id, typename... Parameters, typename ReturnType, class Registry> template typename method::FunctionPointer method::next; template< typename Id, typename... Parameters, typename ReturnType, class Registry> template type_id method::override_impl< Function, FnReturnType>::vp_type_ids[Arity]; template< typename Id, typename... Parameters, typename ReturnType, class Registry> template typename method:: template override_impl method::override_aux< Function, FnReturnType (*)(FnParameters...)>::impl; template< typename Id, typename... Parameters, typename ReturnType, class Registry> method::method() { using namespace policies; this->slots_strides_ptr = slots_strides; if constexpr (!Registry::has_deferred_static_rtti) { resolve_type_ids(); } this->vp_begin = vp_type_ids; this->vp_end = vp_type_ids + Arity; this->not_implemented = reinterpret_cast(fn_not_implemented); this->ambiguous = reinterpret_cast(fn_ambiguous); Registry::methods.push_back(*this); } template< typename Id, typename... Parameters, typename ReturnType, class Registry> void method::resolve_type_ids() { using namespace detail; this->method_type_id = rtti::template static_type(); this->return_type_id = rtti::template static_type>(); init_type_ids< Registry, mp11::mp_transform_q< mp11::mp_bind_back, VirtualParameters>>::fn(this->vp_type_ids); } template< typename Id, typename... Parameters, typename ReturnType, class Registry> method::~method() { Registry::methods.remove(*this); } // ----------------------------------------------------------------------------- // method dispatch template< typename Id, typename... Parameters, typename ReturnType, class Registry> BOOST_FORCEINLINE auto method::operator()( typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS StripVirtualDecorator::type... args) const -> ReturnType { using namespace detail; auto pf = resolve(parameter_traits::peek(args)...); return pf(std::forward::type>( args)...); } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template BOOST_FORCEINLINE typename method::FunctionPointer method::resolve( const ArgType&... args) const { using namespace detail; Registry::require_initialized(); void (*pf)(); if constexpr (Arity == 1) { pf = resolve_uni, ArgType...>(args...).pf; } else { pf = resolve_multi_first, ArgType...>( args...) .pf; } return reinterpret_cast(pf); } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template BOOST_FORCEINLINE auto method::vptr( const ArgType& arg) const -> vptr_type { if constexpr (detail::is_virtual_ptr) { return arg.vptr(); } else { return detail::acquire_vptr(arg); } } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template BOOST_FORCEINLINE auto method::resolve_uni( const ArgType& arg, const MoreArgTypes&... more_args) const -> detail::word { using namespace detail; using namespace policies; using namespace boost::mp11; if constexpr (is_virtual>::value) { vptr_type vtbl = vptr(arg); return vtbl[this->slots_strides[0]]; } else { return resolve_uni>(more_args...); } } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template BOOST_FORCEINLINE auto method::resolve_multi_first( const ArgType& arg, const MoreArgTypes&... more_args) const -> detail::word { using namespace detail; using namespace boost::mp11; if constexpr (is_virtual>::value) { vptr_type vtbl = vptr(arg); std::size_t slot = this->slots_strides[0]; // The first virtual parameter is special. Since its stride is // 1, there is no need to store it. Also, the method table // contains a pointer into the multi-dimensional dispatch table, // already resolved to the appropriate group. auto dispatch = vtbl[slot].pw; return resolve_multi_next<1, mp_rest, MoreArgTypes...>( dispatch, more_args...); } else { return resolve_multi_first, MoreArgTypes...>( more_args...); } } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template< std::size_t VirtualArg, typename MethodArgList, typename ArgType, typename... MoreArgTypes> BOOST_FORCEINLINE auto method::resolve_multi_next( vptr_type dispatch, const ArgType& arg, const MoreArgTypes&... more_args) const -> detail::word { using namespace detail; using namespace boost::mp11; if constexpr (is_virtual>::value) { vptr_type vtbl = vptr(arg); std::size_t slot = this->slots_strides[VirtualArg]; std::size_t stride = this->slots_strides[Arity + VirtualArg - 1]; dispatch = dispatch + vtbl[slot].i * stride; } if constexpr (VirtualArg + 1 == Arity) { return *dispatch; } else { return resolve_multi_next< VirtualArg + 1, mp_rest, MoreArgTypes...>( dispatch, more_args...); } } // ----------------------------------------------------------------------------- // Error handling template< typename Id, typename... Parameters, typename ReturnType, class Registry> template inline auto method::has_next() -> bool { if (next == fn_not_implemented) { return false; } if (next == fn_ambiguous) { return false; } return true; } template< typename Id, typename... Parameters, typename ReturnType, class Registry> BOOST_NORETURN auto method::fn_not_implemented( detail::remove_virtual_... args) -> ReturnType { using namespace policies; if constexpr (Registry::has_error_handler) { no_overrider error; detail::init_bad_call::fn( error, detail::parameter_traits::peek(args)...); Registry::error_handler::error(error); } abort(); // in case user handler "forgets" to abort } template< typename Id, typename... Parameters, typename ReturnType, class Registry> BOOST_NORETURN auto method::fn_ambiguous( detail::remove_virtual_... args) -> ReturnType { using namespace policies; if constexpr (Registry::has_error_handler) { ambiguous_call error; detail::init_bad_call::fn( error, detail::parameter_traits::peek(args)...); Registry::error_handler::error(error); } abort(); // in case user handler "forgets" to abort } // ----------------------------------------------------------------------------- // overriders namespace detail { template struct same_reference_category { static constexpr bool value = (std::is_lvalue_reference::value == std::is_lvalue_reference::value) && (std::is_rvalue_reference::value == std::is_rvalue_reference::value); }; template struct validate_overrider_parameter : std::false_type { static_assert( false_t, "non-virtual parameter types must match exactly"); }; template struct validate_overrider_parameter< T1, T2, std::enable_if_t< is_virtual_ptr && is_virtual_ptr && !same_reference_category::value>> : std::false_type { static_assert( false_t, "different virtual_ptr<> reference categories"); }; template struct validate_overrider_parameter< T1, T2, std::enable_if_t && !is_virtual_ptr>> : std::false_type { static_assert( false_t, "virtual_ptr<> is required in overrider in same position as in " "method"); }; template struct validate_overrider_parameter : std::true_type {}; template struct validate_overrider_parameter, T2, void> : std::true_type {}; template struct validate_overrider_parameter, virtual_, void> : std::false_type { static_assert(false_t, "virtual_<> is not allowed in overriders"); }; template struct validate_overrider_parameter, virtual_ptr, void> : std::true_type {}; template struct validate_overrider_parameter< virtual_ptr, virtual_ptr, void> : std::true_type { static_assert(std::is_same_v, "registry mismatch"); using C1 = virtual_type, R>; using C2 = virtual_type, R>; static_assert( std::is_base_of_v && std::is_convertible_v, virtual_ptr>, "method parameter must be an unambiguous accessible base " "of corresponding overrider parameter"); }; template struct validate_overrider_parameter< const virtual_ptr&, const virtual_ptr&, void> : std::true_type { static_assert(std::is_same_v, "registry mismatch"); using C1 = virtual_type&, R>; using C2 = virtual_type&, R>; static_assert( std::is_base_of_v && std::is_convertible_v, virtual_ptr>, "method parameter must be an unambiguous accessible base " "of corresponding overrider parameter"); }; template struct validate_overrider_parameter< virtual_ptr&&, virtual_ptr&&, void> : std::true_type { static_assert(std::is_same_v, "registry mismatch"); using C1 = virtual_type&&, R>; using C2 = virtual_type&&, R>; static_assert( std::is_base_of_v && std::is_convertible_v, virtual_ptr>, "method parameter must be an unambiguous accessible base " "of corresponding overrider parameter"); }; } // namespace detail template< typename Id, typename... Parameters, typename ReturnType, class Registry> template< auto Overrider, typename OverriderReturn, typename... OverriderParameters> auto method:: thunk::fn( detail::remove_virtual_... arg) -> ReturnType { using namespace detail; static_assert( (validate_overrider_parameter::value && ...), "virtual_ptr category mismatch"); return Overrider( detail::parameter_traits::template cast< OverriderParameters>( std::forward>(arg))...); } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template method::override_impl< Function, FnReturnType>::override_impl(FunctionPointer* p_next) { using namespace detail; // static variable this->method below is zero-initialized but gcc and clang // don't always see that. #ifdef BOOST_CLANG #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wuninitialized" #endif #ifdef BOOST_GCC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuninitialized" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif // zero-initalized static variable // coverity[uninit_use:FALSE] if (overrider_info::method) { BOOST_ASSERT(overrider_info::method == &fn); return; } #ifdef BOOST_CLANG #pragma clang diagnostic pop #endif #ifdef BOOST_GCC #pragma GCC diagnostic pop #endif overrider_info::method = &fn; if constexpr (!Registry::has_deferred_static_rtti) { resolve_type_ids(); } this->next = reinterpret_cast( p_next ? p_next : &method::next); using Thunk = thunk; this->pf = reinterpret_cast(Thunk::fn); this->vp_begin = vp_type_ids; this->vp_end = vp_type_ids + Arity; fn.overriders.push_back(*this); } template< typename Id, typename... Parameters, typename ReturnType, class Registry> template void method::override_impl< Function, FnReturnType>::resolve_type_ids() { using namespace detail; this->return_type = Registry::rtti::template static_type< virtual_type>(); this->type = Registry::rtti::template static_type(); using Thunk = thunk; detail:: init_type_ids::fn( this->vp_type_ids); } //! Aliases for the most frequently used types in the library. namespace aliases { using boost::openmethod::final_virtual_ptr; using boost::openmethod::virtual_; using boost::openmethod::virtual_ptr; } // namespace aliases // ============================================================================== // Exposition only #ifdef __MRDOCS__ //! Blueprint for a specialization of @ref virtual_traits (exposition only). //! //! Specializations of @ref virtual_traits must implement the members listed //! here. //! //! @tparam T The type of a virtual parameter of a method. //! @tparam Registry A @ref registry. template struct VirtualTraits { //! Class to use for dispatch. //! //! Aliases to the class to be considered during method dispatch to determine //! which overrider to select, and which type_id to use for error reporting. //! `virtual_traits::virtual_type` aliases to `Class` if `T` is `Class&`, //! `const Class&`, `Class*`, `const Class*`, `virtual_ptr`, //! `virtual_ptr`, `std::shared_ptr`, //! `std::shared_ptr`, `virtual_ptr>`, //! etc. //! //! @par Requirements //! //! `virtual_type` must be an alias to an *unadorned* *class* type, *not* //! cv-qualified. using virtual_type = detail::unspecified; //! Returns a reference to the object to use for dispatch. //! //! Return a reference to the object to use for dispatch. `arg` may not be //! copied, moved or altered in any way. //! //! @param arg An argument passed to the method call. //! @return A reference to an object. static auto peek(T arg) -> const virtual_type&; //! Casts a virtual argument. //! //! Casts a virtual argument to the type expected by the overrider. //! //! @tparam T The type of a virtual parameter of a method. //! @tparam U The type of a virtual parameter of an overrider. //! @param arg The argument passed to a method call. //! @return A reference to the argument, cast to `U`. template static auto cast(T arg) -> U; //! Rebind to a another class (smart pointers only). //! //! If `T` is a smart pointer, `rebind` is the same kind of smart //! pointer, but pointing to a `U`. //! //! @note `rebind` must be implemented @em only for smart pointer classes //! that can be used as object pointers by @ref virtual_ptr in place of //! plain pointers. //! //! @tparam U The new element type. template using rebind = detail::unspecified; }; #endif } // namespace boost::openmethod #ifdef _MSC_VER #pragma warning(pop) #endif #endif