mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-05 03:43:40 +00:00
314 lines
12 KiB
C++
314 lines
12 KiB
C++
#ifndef ENTT_POLY_POLY_HPP
|
|
#define ENTT_POLY_POLY_HPP
|
|
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#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<class Type>
|
|
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<std::size_t Member, typename... Args>
|
|
poly_inspector invoke(Args &&...args) const;
|
|
|
|
/*! @copydoc invoke */
|
|
template<std::size_t Member, typename... Args>
|
|
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<typename Concept, std::size_t Len, std::size_t Align>
|
|
class poly_vtable {
|
|
using inspector = typename Concept::template type<poly_inspector>;
|
|
|
|
template<typename Ret, typename... Args>
|
|
static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
|
|
|
|
template<typename Ret, typename... Args>
|
|
static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
|
|
|
|
template<typename Ret, typename... Args>
|
|
static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
|
|
|
|
template<typename Ret, typename... Args>
|
|
static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
|
|
|
|
template<typename Ret, typename... Args>
|
|
static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
|
|
|
|
template<auto... Candidate>
|
|
static auto make_vtable(value_list<Candidate...>) noexcept
|
|
-> decltype(std::make_tuple(vtable_entry(Candidate)...));
|
|
|
|
template<typename... Func>
|
|
[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) noexcept {
|
|
if constexpr(sizeof...(Func) == 0u) {
|
|
return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
|
|
} else if constexpr((std::is_function_v<Func> && ...)) {
|
|
return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector::*>())...)){};
|
|
}
|
|
}
|
|
|
|
template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
|
|
static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept {
|
|
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
|
|
entry = +[](Any &, Args... args) -> Ret {
|
|
return std::invoke(Candidate, std::forward<Args>(args)...);
|
|
};
|
|
} else {
|
|
entry = +[](Any &instance, Args... args) -> Ret {
|
|
return static_cast<Ret>(std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(instance), std::forward<Args>(args)...));
|
|
};
|
|
}
|
|
}
|
|
|
|
template<typename Type, auto... Index>
|
|
[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) noexcept {
|
|
vtable_type impl{};
|
|
(fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
|
|
return impl;
|
|
}
|
|
|
|
using vtable_type = decltype(make_vtable(Concept{}));
|
|
static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u;
|
|
|
|
public:
|
|
/*! @brief Virtual table type. */
|
|
using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>;
|
|
|
|
/**
|
|
* @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<typename Type>
|
|
[[nodiscard]] static type instance() noexcept {
|
|
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
|
|
static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
|
|
|
|
if constexpr(is_mono_v) {
|
|
return std::get<0>(vtable);
|
|
} else {
|
|
return &vtable;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Poly base class used to inject functionalities into concepts.
|
|
* @tparam Poly The outermost poly class.
|
|
*/
|
|
template<typename Poly>
|
|
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<std::size_t Member, typename... Args>
|
|
[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const {
|
|
const auto &poly = static_cast<const Poly &>(self);
|
|
|
|
if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
|
|
return poly.vtable(poly.storage, std::forward<Args>(args)...);
|
|
} else {
|
|
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
|
|
/*! @copydoc invoke */
|
|
template<std::size_t Member, typename... Args>
|
|
[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) {
|
|
auto &poly = static_cast<Poly &>(self);
|
|
|
|
if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
|
|
static_assert(Member == 0u, "Unknown member");
|
|
return poly.vtable(poly.storage, std::forward<Args>(args)...);
|
|
} else {
|
|
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Shortcut for calling `poly_base<Type>::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<std::size_t Member, typename Poly, typename... Args>
|
|
decltype(auto) poly_call(Poly &&self, Args &&...args) {
|
|
return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(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.<br/>
|
|
* This class aims to make it simple and easy to use.
|
|
*
|
|
* @note
|
|
* Both deduced and defined static virtual tables are supported.<br/>
|
|
* 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<typename Concept, std::size_t Len, std::size_t Align>
|
|
class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
|
|
/*! @brief A poly base is allowed to snoop into a poly object. */
|
|
friend struct poly_base<basic_poly>;
|
|
|
|
public:
|
|
/*! @brief Concept type. */
|
|
using concept_type = typename Concept::template type<poly_base<basic_poly>>;
|
|
/*! @brief Virtual table type. */
|
|
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
|
|
|
|
/*! @brief Default constructor. */
|
|
basic_poly() 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<typename Type, typename... Args>
|
|
explicit basic_poly(std::in_place_type_t<Type>, Args &&...args)
|
|
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
|
|
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {}
|
|
|
|
/**
|
|
* @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<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
|
|
basic_poly(Type &&value) noexcept
|
|
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
|
|
|
|
/**
|
|
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
|
* @return The object type if any, `type_id<void>()` otherwise.
|
|
*/
|
|
[[nodiscard]] const type_info &type() const 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 noexcept {
|
|
return storage.data();
|
|
}
|
|
|
|
/*! @copydoc data */
|
|
[[nodiscard]] void *data() 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<typename Type, typename... Args>
|
|
void emplace(Args &&...args) {
|
|
storage.template emplace<Type>(std::forward<Args>(args)...);
|
|
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
|
}
|
|
|
|
/*! @brief Destroys contained object */
|
|
void reset() {
|
|
storage.reset();
|
|
vtable = {};
|
|
}
|
|
|
|
/**
|
|
* @brief Returns false if a poly is empty, true otherwise.
|
|
* @return False if the poly is empty, true otherwise.
|
|
*/
|
|
[[nodiscard]] explicit operator bool() const noexcept {
|
|
return static_cast<bool>(storage);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns a pointer to the underlying concept.
|
|
* @return A pointer to the underlying concept.
|
|
*/
|
|
[[nodiscard]] concept_type *operator->() noexcept {
|
|
return this;
|
|
}
|
|
|
|
/*! @copydoc operator-> */
|
|
[[nodiscard]] const concept_type *operator->() const noexcept {
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @brief Aliasing constructor.
|
|
* @return A poly that shares a reference to an unmanaged object.
|
|
*/
|
|
[[nodiscard]] basic_poly as_ref() noexcept {
|
|
basic_poly ref{};
|
|
ref.storage = storage.as_ref();
|
|
ref.vtable = vtable;
|
|
return ref;
|
|
}
|
|
|
|
/*! @copydoc as_ref */
|
|
[[nodiscard]] basic_poly as_ref() const noexcept {
|
|
basic_poly ref{};
|
|
ref.storage = storage.as_ref();
|
|
ref.vtable = vtable;
|
|
return ref;
|
|
}
|
|
|
|
private:
|
|
basic_any<Len, Align> storage;
|
|
vtable_type vtable;
|
|
};
|
|
|
|
} // namespace entt
|
|
|
|
#endif
|