#ifndef ENTT_POLY_POLY_HPP #define ENTT_POLY_POLY_HPP #include #include #include #include #include #include "../config/config.h" #include "../core/any.hpp" #include "../core/type_info.hpp" #include "../core/type_traits.hpp" #include "fwd.hpp" namespace entt { /*! @brief Inspector class used to infer the type of the virtual table. */ struct poly_inspector { /** * @brief Generic conversion operator (definition only). * @tparam Type Type to which conversion is requested. */ template operator Type &&() const; /** * @brief Dummy invocation function (definition only). * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param args The arguments to pass to the function. * @return A poly inspector convertible to any type. */ template poly_inspector invoke(Args &&... args) const; /*! @copydoc invoke */ template poly_inspector invoke(Args &&... args); }; /** * @brief Static virtual table factory. * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. */ template class poly_vtable { using inspector = typename Concept::template type; template static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(basic_any &, Args...); template static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const basic_any &, Args...); template static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const basic_any &, Args...); template static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(basic_any &, Args...); template static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const basic_any &, Args...); template static auto make_vtable(value_list) -> decltype(std::make_tuple(vtable_entry(Candidate)...)); template [[nodiscard]] static constexpr auto make_vtable(type_list) { if constexpr(sizeof...(Func) == 0) { return decltype(make_vtable(typename Concept::template impl{})){}; } else if constexpr((std::is_function_v && ...)) { return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; } } template static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) { if constexpr(std::is_invocable_r_v) { entry = +[](Any &, Args... args) -> Ret { return std::invoke(Candidate, std::forward(args)...); }; } else { entry = +[](Any &instance, Args... args) -> Ret { return static_cast(std::invoke(Candidate, any_cast &>(instance), std::forward(args)...)); }; } } template [[nodiscard]] static auto fill_vtable(std::index_sequence) { type impl{}; (fill_vtable_entry>>(std::get(impl)), ...); return impl; } public: /*! @brief Virtual table type. */ using type = decltype(make_vtable(Concept{})); /** * @brief Returns a static virtual table for a specific concept and type. * @tparam Type The type for which to generate the virtual table. * @return A static virtual table for the given concept and type. */ template [[nodiscard]] static const auto * instance() { static_assert(std::is_same_v>, "Type differs from its decayed form"); static const auto vtable = fill_vtable(std::make_index_sequence::size>{}); return &vtable; } }; /** * @brief Poly base class used to inject functionalities into concepts. * @tparam Poly The outermost poly class. */ template struct poly_base { /** * @brief Invokes a function from the static virtual table. * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const { const auto &poly = static_cast(self); return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } /*! @copydoc invoke */ template [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) { auto &poly = static_cast(self); return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } }; /** * @brief Shortcut for calling `poly_base::invoke`. * @tparam Member Index of the function to invoke. * @tparam Poly A fully defined poly object. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template decltype(auto) poly_call(Poly &&self, Args &&... args) { return std::forward(self).template invoke(self, std::forward(args)...); } /** * @brief Static polymorphism made simple and within everyone's reach. * * Static polymorphism is a very powerful tool in C++, albeit sometimes * cumbersome to obtain.
* This class aims to make it simple and easy to use. * * @note * Both deduced and defined static virtual tables are supported.
* Moreover, the `poly` class template also works with unmanaged objects. * * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_poly: private Concept::template type>> { /*! @brief A poly base is allowed to snoop into a poly object. */ friend struct poly_base; using vtable_type = typename poly_vtable::type; public: /*! @brief Concept type. */ using concept_type = typename Concept::template type>; /*! @brief Default constructor. */ basic_poly() ENTT_NOEXCEPT : storage{}, vtable{} {} /** * @brief Constructs a poly by directly initializing the new object. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_poly(std::in_place_type_t, Args &&... args) : storage{std::in_place_type, std::forward(args)...}, vtable{poly_vtable::template instance>>()} {} /** * @brief Constructs a poly from a given value. * @tparam Type Type of object to use to initialize the poly. * @param value An instance of an object to use to initialize the poly. */ template>, basic_poly>>> basic_poly(Type &&value) ENTT_NOEXCEPT : basic_poly{std::in_place_type>>, std::forward(value)} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_poly(const basic_poly &other) = default; /** * @brief Move constructor. * @param other The instance to move from. */ basic_poly(basic_poly &&other) ENTT_NOEXCEPT : basic_poly{} { swap(*this, other); } /** * @brief Assignment operator. * @param other The instance to assign from. * @return This poly object. */ basic_poly & operator=(basic_poly other) { swap(other, *this); return *this; } /** * @brief Returns the type of the contained object. * @return The type of the contained object, if any. */ [[nodiscard]] type_info type() const ENTT_NOEXCEPT { return storage.type(); } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void * data() const ENTT_NOEXCEPT { return storage.data(); } /*! @copydoc data */ [[nodiscard]] void * data() ENTT_NOEXCEPT { return storage.data(); } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&... args) { *this = basic_poly{std::in_place_type, std::forward(args)...}; } /*! @brief Destroys contained object */ void reset() { *this = basic_poly{}; } /** * @brief Returns false if a poly is empty, true otherwise. * @return False if the poly is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(vtable == nullptr); } /** * @brief Returns a pointer to the underlying concept. * @return A pointer to the underlying concept. */ [[nodiscard]] concept_type * operator->() ENTT_NOEXCEPT { return this; } /*! @copydoc operator-> */ [[nodiscard]] const concept_type * operator->() const ENTT_NOEXCEPT { return this; } /** * @brief Swaps two poly objects. * @param lhs A valid poly object. * @param rhs A valid poly object. */ friend void swap(basic_poly &lhs, basic_poly &rhs) { using std::swap; swap(lhs.storage, rhs.storage); swap(lhs.vtable, rhs.vtable); } /** * @brief Aliasing constructor. * @return A poly that shares a reference to an unmanaged object. */ [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { basic_poly ref = std::as_const(*this).as_ref(); ref.storage = storage.as_ref(); return ref; } /*! @copydoc as_ref */ [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; return ref; } private: basic_any storage; const vtable_type *vtable; }; } #endif