core/entity/meta/poly:
* updated poly (static vtable, design review) * .ref -> as_ref (any, meta_any, poly) * poly storage
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief A type-safe container for single values of any type. */
|
||||
/*! @brief A SBO friendly, type-safe container for single values of any type. */
|
||||
class any {
|
||||
enum class operation { COPY, MOVE, DTOR, ADDR, REF, TYPE };
|
||||
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
*/
|
||||
template<typename Type>
|
||||
any(std::reference_wrapper<Type> value)
|
||||
any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
|
||||
: vtable{&basic_vtable<std::add_lvalue_reference_t<Type>>},
|
||||
instance{&value.get()}
|
||||
{}
|
||||
@@ -212,16 +212,6 @@ public:
|
||||
*this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return An any that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] any ref() const ENTT_NOEXCEPT {
|
||||
any other{};
|
||||
vtable(operation::REF, *this, &other);
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if a wrapper is empty, true otherwise.
|
||||
* @return False if the wrapper is empty, true otherwise.
|
||||
@@ -243,6 +233,17 @@ public:
|
||||
std::swap(lhs.vtable, rhs.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @param other A reference to an object that isn't necessarily initialized.
|
||||
* @return An any that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] friend any as_ref(const any &other) ENTT_NOEXCEPT {
|
||||
any ref{};
|
||||
other.vtable(operation::REF, other, &ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
private:
|
||||
vtable_type *vtable;
|
||||
union { void *instance; storage_type storage; };
|
||||
|
||||
83
src/entt/entity/poly_storage.hpp
Normal file
83
src/entt/entity/poly_storage.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef ENTT_ENTITY_POLY_STORAGE_HPP
|
||||
#define ENTT_ENTITY_POLY_STORAGE_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../poly/poly.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic poly storage implementation.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct Storage: type_list<
|
||||
type_info() const ENTT_NOEXCEPT,
|
||||
void(basic_registry<Entity> &, const Entity *, const Entity *)
|
||||
> {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
|
||||
/**
|
||||
* @brief Concept definition.
|
||||
* @tparam Base Opaque base class from which to inherit.
|
||||
*/
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
/**
|
||||
* @brief Returns a type info for the contained objects.
|
||||
* @return The type info for the contained objects.
|
||||
*/
|
||||
type_info value_type() const ENTT_NOEXCEPT {
|
||||
return poly_call<0>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes entities from a storage.
|
||||
* @param owner The registry that issued the request.
|
||||
* @param first An iterator to the first element of the range of
|
||||
* entities.
|
||||
* @param last An iterator past the last element of the range of
|
||||
* entities.
|
||||
*/
|
||||
void remove(basic_registry<entity_type> &owner, const entity_type *first, const entity_type *last) {
|
||||
poly_call<1>(*this, owner, first, last);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concept implementation.
|
||||
* @tparam Type Type for which to generate an implementation.
|
||||
*/
|
||||
template<typename Type>
|
||||
using impl = value_list<
|
||||
&type_id<typename Type::value_type>,
|
||||
&Type::template remove<const entity_type *>
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the poly storage type associate with a given entity type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity, typename = void>
|
||||
struct poly_storage_traits {
|
||||
/*! @brief Poly storage type for the given entity type. */
|
||||
using storage_type = poly<Storage<Entity>>;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "group.hpp"
|
||||
#include "poly_storage.hpp"
|
||||
#include "runtime_view.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "storage.hpp"
|
||||
@@ -41,14 +43,14 @@ namespace entt {
|
||||
template<typename Entity>
|
||||
class basic_registry {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
using poly_storage_type = typename poly_storage_traits<Entity>::storage_type;
|
||||
|
||||
template<typename Component>
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
|
||||
|
||||
struct pool_data {
|
||||
type_info info{};
|
||||
poly_storage_type poly;
|
||||
std::unique_ptr<basic_sparse_set<Entity>> pool{};
|
||||
void(* remove)(basic_sparse_set<Entity> &, basic_registry &, const Entity *, const Entity *){};
|
||||
};
|
||||
|
||||
template<typename...>
|
||||
@@ -115,11 +117,8 @@ class basic_registry {
|
||||
}
|
||||
|
||||
if(auto &&pdata = pools[index]; !pdata.pool) {
|
||||
pdata.info = type_id<Component>();
|
||||
pdata.pool.reset(new storage_type<Component>());
|
||||
pdata.remove = +[](basic_sparse_set<Entity> &cpool, basic_registry &owner, const Entity *first, const Entity *last) {
|
||||
static_cast<storage_type<Component> &>(cpool).remove(owner, first, last);
|
||||
};
|
||||
pdata.poly = std::ref(*static_cast<storage_type<Component> *>(pdata.pool.get()));
|
||||
}
|
||||
|
||||
return static_cast<storage_type<Component> *>(pools[index].pool.get());
|
||||
@@ -158,6 +157,8 @@ public:
|
||||
using version_type = typename traits_type::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Poly storage type. */
|
||||
using poly_storage = typename poly_storage_traits<Entity>::storage_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_registry() = default;
|
||||
@@ -178,6 +179,15 @@ public:
|
||||
static_cast<void>(assure<Component>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TODO
|
||||
* @param info TODO
|
||||
* @return TODO
|
||||
*/
|
||||
poly_storage storage(const type_info info) {
|
||||
return info.seq() < pools.size() ? pools[info.seq()].poly : poly_storage{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of existing components of the given type.
|
||||
* @tparam Component Type of component of which to return the size.
|
||||
@@ -765,7 +775,7 @@ public:
|
||||
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) {
|
||||
pdata.remove(*pdata.pool, *this, std::begin(wrap), std::end(wrap));
|
||||
pdata.poly->remove(*this, std::begin(wrap), std::end(wrap));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -912,8 +922,8 @@ public:
|
||||
void clear() {
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
if(const auto &pdata = pools[pos-1]; pdata.pool) {
|
||||
pdata.remove(*pdata.pool, *this, pdata.pool->data(), pdata.pool->data() + pdata.pool->size());
|
||||
if(auto &pdata = pools[pos-1]; pdata.pool) {
|
||||
pdata.poly->remove(*this, pdata.pool->rbegin(), pdata.pool->rend());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1149,12 +1159,12 @@ public:
|
||||
std::vector<const basic_sparse_set<Entity> *> filter(std::distance(from, to));
|
||||
|
||||
std::transform(first, last, component.begin(), [this](const auto ctype) {
|
||||
const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.info.hash() == ctype; });
|
||||
const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; });
|
||||
return it == pools.cend() ? nullptr : it->pool.get();
|
||||
});
|
||||
|
||||
std::transform(from, to, filter.begin(), [this](const auto ctype) {
|
||||
const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.info.hash() == ctype; });
|
||||
const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; });
|
||||
return it == pools.cend() ? nullptr : it->pool.get();
|
||||
});
|
||||
|
||||
@@ -1465,7 +1475,7 @@ public:
|
||||
void visit(entity_type entity, Func func) const {
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
if(const auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) {
|
||||
func(pdata.info);
|
||||
func(pdata.poly->value_type());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1494,7 +1504,7 @@ public:
|
||||
void visit(Func func) const {
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
if(const auto &pdata = pools[pos-1]; pdata.pool) {
|
||||
func(pdata.info);
|
||||
func(pdata.poly->value_type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -787,8 +787,7 @@ private:
|
||||
|
||||
|
||||
/**
|
||||
* @brief Applies component-to-storage conversion and defines the resulting type
|
||||
* as the member typedef type.
|
||||
* @brief Defines the component-to-storage conversion.
|
||||
*
|
||||
* Formally:
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/organizer.hpp"
|
||||
#include "entity/poly_storage.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/runtime_view.hpp"
|
||||
#include "entity/snapshot.hpp"
|
||||
|
||||
@@ -157,12 +157,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Opaque wrapper for values of any type.
|
||||
*
|
||||
* This class uses a technique called small buffer optimization (SBO) to get rid
|
||||
* of memory allocations if possible. This should improve overall performance.
|
||||
*/
|
||||
/*! @brief Opaque wrapper for values of any type. */
|
||||
class meta_any {
|
||||
enum class operation { DEREF, SEQ, ASSOC };
|
||||
|
||||
@@ -424,18 +419,6 @@ public:
|
||||
*this = meta_any{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return A meta any that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] meta_any ref() const ENTT_NOEXCEPT {
|
||||
meta_any other{};
|
||||
other.node = node;
|
||||
other.storage = storage.ref();
|
||||
other.vtable = vtable;
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sequence container proxy.
|
||||
* @return A sequence container proxy for the underlying object.
|
||||
@@ -496,6 +479,19 @@ public:
|
||||
swap(lhs.node, rhs.node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @param other A reference to an object that isn't necessarily initialized.
|
||||
* @return A meta any that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] friend meta_any as_ref(const meta_any &other) ENTT_NOEXCEPT {
|
||||
meta_any ref{};
|
||||
ref.node = other.node;
|
||||
ref.storage = as_ref(other.storage);
|
||||
ref.vtable = other.vtable;
|
||||
return ref;
|
||||
}
|
||||
|
||||
private:
|
||||
any storage;
|
||||
vtable_type *vtable;
|
||||
@@ -535,7 +531,7 @@ struct meta_handle {
|
||||
: meta_handle{}
|
||||
{
|
||||
if constexpr(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>) {
|
||||
any = value.ref();
|
||||
any = as_ref(value);
|
||||
} else {
|
||||
any = std::ref(value);
|
||||
}
|
||||
@@ -1673,7 +1669,7 @@ public:
|
||||
|
||||
/*! @brief Pre-increment operator. @return This iterator. */
|
||||
meta_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return next_fn(handle.ref()), *this;
|
||||
return next_fn(as_ref(handle)), *this;
|
||||
}
|
||||
|
||||
/*! @brief Post-increment operator. @return This iterator. */
|
||||
@@ -1707,7 +1703,7 @@ public:
|
||||
* @return The element to which the meta pointer points.
|
||||
*/
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return get_fn(handle.ref());
|
||||
return get_fn(as_ref(handle));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1838,7 +1834,7 @@ inline bool meta_sequence_container::clear() {
|
||||
* case of success) and a bool denoting whether the insertion took place.
|
||||
*/
|
||||
inline std::pair<meta_sequence_container::iterator, bool> meta_sequence_container::insert(iterator it, meta_any value) {
|
||||
return insert_fn(instance, it, value.ref());
|
||||
return insert_fn(instance, it, as_ref(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -1931,7 +1927,7 @@ public:
|
||||
|
||||
/*! @brief Pre-increment operator. @return This iterator. */
|
||||
meta_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return next_fn(handle.ref()), *this;
|
||||
return next_fn(as_ref(handle)), *this;
|
||||
}
|
||||
|
||||
/*! @brief Post-increment operator. @return This iterator. */
|
||||
@@ -1965,7 +1961,7 @@ public:
|
||||
* @return The element to which the meta pointer points.
|
||||
*/
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return { key_fn(handle.ref()), value_fn(handle.ref()) };
|
||||
return { key_fn(as_ref(handle)), value_fn(as_ref(handle)) };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2117,7 +2113,7 @@ inline bool meta_associative_container::clear() {
|
||||
* @return A bool denoting whether the insertion took place.
|
||||
*/
|
||||
inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
|
||||
return insert_fn(instance, key.ref(), value.ref());
|
||||
return insert_fn(instance, as_ref(key), as_ref(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -2127,7 +2123,7 @@ inline bool meta_associative_container::insert(meta_any key, meta_any value = {}
|
||||
* @return A bool denoting whether the removal took place.
|
||||
*/
|
||||
inline bool meta_associative_container::erase(meta_any key) {
|
||||
return erase_fn(instance, key.ref());
|
||||
return erase_fn(instance, as_ref(key));
|
||||
}
|
||||
|
||||
|
||||
@@ -2138,7 +2134,7 @@ inline bool meta_associative_container::erase(meta_any key) {
|
||||
* @return An iterator to the element with the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
|
||||
return find_fn(instance, key.ref());
|
||||
return find_fn(instance, as_ref(key));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,65 +15,6 @@
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Inline variable designed to contain the definition of a concept.
|
||||
* @tparam Concept A concept class template.
|
||||
* @tparam Type The type for which the definition is provided.
|
||||
*/
|
||||
template<template<typename> class Concept, typename Type>
|
||||
inline constexpr auto poly_impl = value_list{};
|
||||
|
||||
|
||||
/*! @brief Static virtual table factory. */
|
||||
class poly_vtable {
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(*)(Type &, Args...)) {
|
||||
return +[](any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<Type &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(*)(const Type &, Args...)) {
|
||||
return +[](const any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<std::add_const_t<Type> &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(Type:: *)(Args...)) {
|
||||
return +[](any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<Type &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename... Args>
|
||||
[[nodiscard]] static auto * vtable_entry(Ret(Type:: *)(Args...) const) {
|
||||
return +[](const any &any, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, any_cast<std::add_const_t<Type> &>(any), std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto... Impl>
|
||||
[[nodiscard]] static auto * instance(value_list<Impl...>) {
|
||||
static const auto vtable = std::make_tuple(vtable_entry<Type, Impl>(Impl)...);
|
||||
return &vtable;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Returns a static virtual table for a specific concept and type.
|
||||
* @tparam Concept A concept class template.
|
||||
* @tparam Type The type for which to generate the virtual table.
|
||||
* @return A static virtual table for the given concept and type.
|
||||
*/
|
||||
template<template<typename> class Concept, typename Type>
|
||||
[[nodiscard]] static auto * instance() {
|
||||
return instance<Type>(poly_impl<Concept, Type>);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Inspector class used to infer the type of the virtual table. */
|
||||
struct poly_inspector {
|
||||
/**
|
||||
@@ -99,6 +40,77 @@ struct poly_inspector {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Static virtual table factory.
|
||||
* @tparam Concept Concept descriptor.
|
||||
*/
|
||||
template<typename Concept>
|
||||
class poly_vtable {
|
||||
using inspector = typename Concept::template type<poly_inspector>;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(any &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const any &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const any &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(any &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const any &, Args...);
|
||||
|
||||
template<auto... Candidate>
|
||||
static auto make_vtable(value_list<Candidate...>)
|
||||
-> std::tuple<decltype(vtable_entry(Candidate))...>;
|
||||
|
||||
template<typename... Func>
|
||||
[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) {
|
||||
if constexpr(sizeof...(Func) == 0) {
|
||||
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...)) {
|
||||
entry = +[](Any &any, Args... args) -> Ret {
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
|
||||
return std::invoke(Candidate, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(any), std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Type, auto... Candidate, auto... Index>
|
||||
[[nodiscard]] static auto fill_vtable(value_list<Candidate...>, std::index_sequence<Index...>) {
|
||||
type impl{};
|
||||
(fill_vtable_entry<Type, Candidate>(std::get<Index>(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<typename Type>
|
||||
[[nodiscard]] static const auto * instance() {
|
||||
static const auto vtable = fill_vtable<Type>(typename Concept::template impl<Type>{}, std::make_index_sequence<std::tuple_size_v<type>>{});
|
||||
return &vtable;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Poly base class used to inject functionalities into concepts.
|
||||
* @tparam Poly The outermost poly class.
|
||||
@@ -141,7 +153,7 @@ template<auto 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.
|
||||
@@ -150,45 +162,23 @@ decltype(auto) poly_call(Poly &&self, Args &&... args) {
|
||||
* cumbersome to obtain.<br/>
|
||||
* This class aims to make it simple and easy to use.
|
||||
*
|
||||
* Below is a minimal example of use:
|
||||
* @note
|
||||
* Both deduced and defined static virtual tables are supported.<br/>
|
||||
* Moreover, the `poly` class template also works with unmanaged objects.
|
||||
*
|
||||
* ```cpp
|
||||
* template<typename Base>
|
||||
* struct Drawable: Base {
|
||||
* void draw() { entt::poly_call<0>(*this); }
|
||||
* };
|
||||
*
|
||||
* template<typename Type>
|
||||
* inline constexpr auto entt::poly_impl<Drawable, Type> = entt::value_list<&Type::draw>{};
|
||||
*
|
||||
* using drawable = entt::poly<Drawable>;
|
||||
*
|
||||
* struct circle { void draw() {} };
|
||||
* struct square { void draw() {} };
|
||||
*
|
||||
* int main() {
|
||||
* drawable d{circle{}};
|
||||
* d.draw();
|
||||
*
|
||||
* d = square{};
|
||||
* d.draw();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The `poly` class template also supports aliasing for unmanaged objects.
|
||||
* Moreover, thanks to small buffer optimization, it limits the number of
|
||||
* allocations to a minimum where possible.
|
||||
*
|
||||
* @tparam Concept Concept class template.
|
||||
* @tparam Concept Concept descriptor.
|
||||
*/
|
||||
template<template<typename> class Concept>
|
||||
class poly: public Concept<poly_base<poly<Concept>>> {
|
||||
template<typename Concept>
|
||||
class poly: private Concept::template type<poly_base<poly<Concept>>> {
|
||||
/*! @brief A poly base is allowed to snoop into a poly object. */
|
||||
friend struct poly_base<poly<Concept>>;
|
||||
|
||||
using vtable_t = std::remove_pointer_t<decltype(poly_vtable::instance<Concept, Concept<poly_inspector>>())>;
|
||||
using vtable_type = typename poly_vtable<Concept>::type;
|
||||
|
||||
public:
|
||||
/*! @brief Concept interface type. */
|
||||
using interface = typename Concept::template type<poly_base<poly<Concept>>>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
poly() ENTT_NOEXCEPT
|
||||
: storage{},
|
||||
@@ -204,7 +194,7 @@ public:
|
||||
template<typename Type, typename... Args>
|
||||
explicit poly(std::in_place_type_t<Type>, Args &&... args)
|
||||
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
|
||||
vtable{poly_vtable::instance<Concept, Type>()}
|
||||
vtable{poly_vtable<Concept>::template instance<Type>()}
|
||||
{}
|
||||
|
||||
/**
|
||||
@@ -215,7 +205,7 @@ public:
|
||||
template<typename Type>
|
||||
poly(std::reference_wrapper<Type> value)
|
||||
: storage{value},
|
||||
vtable{poly_vtable::instance<Concept, Type>()}
|
||||
vtable{poly_vtable<Concept>::template instance<Type>()}
|
||||
{}
|
||||
|
||||
/**
|
||||
@@ -224,7 +214,7 @@ public:
|
||||
* @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>>, poly>>>
|
||||
poly(Type &&value)
|
||||
poly(Type &&value) ENTT_NOEXCEPT
|
||||
: poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
|
||||
{}
|
||||
|
||||
@@ -238,7 +228,7 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
poly(poly &&other)
|
||||
poly(poly &&other) ENTT_NOEXCEPT
|
||||
: poly{}
|
||||
{
|
||||
swap(*this, other);
|
||||
@@ -284,18 +274,7 @@ public:
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&... args) {
|
||||
storage.emplace<Type>(std::forward<Args>(args)...);
|
||||
vtable = poly_vtable::instance<Concept, Type>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return A poly that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] poly ref() const ENTT_NOEXCEPT {
|
||||
poly other{};
|
||||
other.storage = storage.ref();
|
||||
other.vtable = vtable;
|
||||
return other;
|
||||
vtable = poly_vtable<Concept>::template instance<Type>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,6 +285,19 @@ public:
|
||||
return !(vtable == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying concept.
|
||||
* @return A pointer to the underlying concept.
|
||||
*/
|
||||
[[nodiscard]] interface * operator->() ENTT_NOEXCEPT {
|
||||
return this;
|
||||
}
|
||||
|
||||
/*! @copydoc operator-> */
|
||||
[[nodiscard]] const interface * operator->() const ENTT_NOEXCEPT {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two poly objects.
|
||||
* @param lhs A valid poly object.
|
||||
@@ -317,9 +309,21 @@ public:
|
||||
swap(lhs.vtable, rhs.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @param other A reference to an object that isn't necessarily initialized.
|
||||
* @return A poly that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] friend poly as_ref(const poly &other) ENTT_NOEXCEPT {
|
||||
poly ref;
|
||||
ref.storage = as_ref(other.storage);
|
||||
ref.vtable = other.vtable;
|
||||
return ref;
|
||||
}
|
||||
|
||||
private:
|
||||
any storage;
|
||||
const vtable_t *vtable;
|
||||
const vtable_type *vtable;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ SETUP_BASIC_TEST(group entt/entity/group.cpp)
|
||||
SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
|
||||
SETUP_BASIC_TEST(helper entt/entity/helper.cpp)
|
||||
SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
|
||||
SETUP_BASIC_TEST(poly_storage entt/entity/poly_storage.cpp)
|
||||
SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
|
||||
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
|
||||
SETUP_BASIC_TEST(registry_no_eto entt/entity/registry_no_eto.cpp ENTT_NO_ETO)
|
||||
@@ -199,7 +200,8 @@ SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
|
||||
|
||||
# Test poly
|
||||
|
||||
SETUP_BASIC_TEST(poly entt/poly/poly.cpp)
|
||||
SETUP_BASIC_TEST(poly_deduced entt/poly/poly_deduced.cpp)
|
||||
SETUP_BASIC_TEST(poly_defined entt/poly/poly_defined.cpp)
|
||||
|
||||
# Test process
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ TEST(Any, SBOInPlaceTypeConstruction) {
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
|
||||
auto other = any.ref();
|
||||
auto other = as_ref(any);
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
@@ -72,7 +72,7 @@ TEST(Any, SBOAsRefConstruction) {
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
ASSERT_EQ(any.data(), &value);
|
||||
|
||||
auto other = any.ref();
|
||||
auto other = as_ref(any);
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
@@ -151,7 +151,7 @@ TEST(Any, NoSBOInPlaceTypeConstruction) {
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
|
||||
auto other = any.ref();
|
||||
auto other = as_ref(any);
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
@@ -169,7 +169,7 @@ TEST(Any, NoSBOAsRefConstruction) {
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
ASSERT_EQ(any.data(), &instance);
|
||||
|
||||
auto other = any.ref();
|
||||
auto other = as_ref(any);
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
|
||||
135
test/entt/entity/poly_storage.cpp
Normal file
135
test/entt/entity/poly_storage.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <cstddef>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/utility.hpp>
|
||||
#include <entt/entity/poly_storage.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
template<typename... Type>
|
||||
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
|
||||
|
||||
template<typename Entity>
|
||||
struct PolyStorage: entt::type_list_cat_t<
|
||||
decltype(as_type_list(std::declval<entt::Storage<Entity>>())),
|
||||
entt::type_list<
|
||||
void(entt::basic_registry<Entity> &, const Entity, const void *),
|
||||
const void *(const Entity) const,
|
||||
const Entity *() const,
|
||||
const void *() const,
|
||||
std::size_t() const,
|
||||
void(entt::basic_registry<Entity> &, const Entity *, const void *, const std::size_t)
|
||||
>
|
||||
> {
|
||||
using entity_type = Entity;
|
||||
using size_type = std::size_t;
|
||||
|
||||
template<typename Base>
|
||||
struct type: entt::Storage<Entity>::template type<Base> {
|
||||
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<entt::Storage<Entity>>::type>;
|
||||
|
||||
void emplace(entt::basic_registry<entity_type> &owner, const entity_type entity, const void *instance) {
|
||||
entt::poly_call<base + 0>(*this, owner, entity, instance);
|
||||
}
|
||||
|
||||
const void * get(const entity_type entity) const {
|
||||
return entt::poly_call<base + 1>(*this, entity);
|
||||
}
|
||||
|
||||
const entity_type * data() const {
|
||||
return entt::poly_call<base + 2>(*this);
|
||||
}
|
||||
|
||||
const void * raw() const {
|
||||
return entt::poly_call<base + 3>(*this);
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
return entt::poly_call<base + 4>(*this);
|
||||
}
|
||||
|
||||
void insert(entt::basic_registry<Entity> &owner, const Entity *entity, const void *instance, const std::size_t length) {
|
||||
entt::poly_call<base + 5>(*this, owner, entity, instance, length);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct members {
|
||||
static void emplace(Type &self, entt::basic_registry<entity_type> &owner, const entity_type entity, const void *instance) {
|
||||
self.emplace(owner, entity, *static_cast<const typename Type::value_type *>(instance));
|
||||
}
|
||||
|
||||
static const typename Type::value_type * get(const Type &self, const entity_type entity) {
|
||||
return &self.get(entity);
|
||||
}
|
||||
|
||||
static void insert(Type &self, entt::basic_registry<entity_type> &owner, const entity_type *entity, const void *instance, const size_type length) {
|
||||
const auto *value = static_cast<const typename Type::value_type *>(instance);
|
||||
self.insert(owner, entity, entity + length, value, value + length);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list_cat_t<
|
||||
typename entt::Storage<Entity>::template impl<Type>,
|
||||
entt::value_list<
|
||||
&members<Type>::emplace,
|
||||
&members<Type>::get,
|
||||
&Type::data,
|
||||
entt::overload<const typename Type::value_type *() const ENTT_NOEXCEPT>(&Type::raw),
|
||||
&Type::size,
|
||||
&members<Type>::insert
|
||||
>
|
||||
>;
|
||||
};
|
||||
|
||||
template<typename Entity>
|
||||
struct entt::poly_storage_traits<Entity> {
|
||||
using storage_type = entt::poly<PolyStorage<Entity>>;
|
||||
};
|
||||
|
||||
TEST(PolyStorage, CopyEntity) {
|
||||
entt::registry registry;
|
||||
const auto entity = registry.create();
|
||||
const auto other = registry.create();
|
||||
|
||||
registry.emplace<int>(entity, 42);
|
||||
registry.emplace<char>(entity, 'c');
|
||||
|
||||
ASSERT_TRUE((registry.has<int, char>(entity)));
|
||||
ASSERT_FALSE((registry.any<int, char>(other)));
|
||||
|
||||
registry.visit(entity, [&](const auto info) {
|
||||
auto storage = registry.storage(info);
|
||||
storage->emplace(registry, other, storage->get(entity));
|
||||
});
|
||||
|
||||
ASSERT_TRUE((registry.has<int, char>(entity)));
|
||||
ASSERT_TRUE((registry.has<int, char>(other)));
|
||||
|
||||
ASSERT_EQ(registry.get<int>(entity), registry.get<int>(other));
|
||||
ASSERT_EQ(registry.get<char>(entity), registry.get<char>(other));
|
||||
}
|
||||
|
||||
TEST(PolyStorage, CopyRegistry) {
|
||||
entt::registry registry;
|
||||
entt::registry other;
|
||||
entt::entity entities[10u];
|
||||
|
||||
registry.create(std::begin(entities), std::end(entities));
|
||||
registry.insert<int>(std::begin(entities), std::end(entities), 42);
|
||||
registry.insert<char>(std::begin(entities), std::end(entities), 'c');
|
||||
|
||||
other.prepare<int>();
|
||||
other.prepare<char>();
|
||||
|
||||
ASSERT_EQ(registry.size(), 10u);
|
||||
ASSERT_EQ(other.size(), 0u);
|
||||
|
||||
other.assign(registry.data(), registry.data() + registry.size(), registry.destroyed());
|
||||
|
||||
registry.visit([&](const auto info) {
|
||||
auto storage = registry.storage(info);
|
||||
other.storage(info)->insert(other, storage->data(), storage->raw(), storage->size());
|
||||
});
|
||||
|
||||
ASSERT_EQ(registry.size(), other.size());
|
||||
}
|
||||
@@ -337,7 +337,7 @@ TEST_F(MetaFunc, ByReference) {
|
||||
int value = 4;
|
||||
|
||||
ASSERT_EQ(func.invoke({}, std::ref(value)).cast<int>(), 8);
|
||||
ASSERT_EQ(func.invoke({}, any.ref()).cast<int>(), 6);
|
||||
ASSERT_EQ(func.invoke({}, as_ref(any)).cast<int>(), 6);
|
||||
ASSERT_EQ(any.cast<int>(), 6);
|
||||
ASSERT_EQ(value, 8);
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/poly/poly.hpp>
|
||||
|
||||
template<typename Base>
|
||||
struct concept: Base {
|
||||
void incr() { entt::poly_call<0>(*this); }
|
||||
void set(int v) { entt::poly_call<1>(*this, v); }
|
||||
int get() const { return entt::poly_call<2>(*this); }
|
||||
void decr() { entt::poly_call<3>(*this); }
|
||||
int mul(int v) { return entt::poly_call<4>(*this, v); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
void decr(Type &self) {
|
||||
self.set(self.get()-1);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
int mul(const Type &self, int v) {
|
||||
return v * self.get();
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr auto entt::poly_impl<concept, Type> =
|
||||
entt::value_list<
|
||||
&Type::incr,
|
||||
&Type::set,
|
||||
&Type::get,
|
||||
&decr<Type>,
|
||||
&mul<Type>
|
||||
>{};
|
||||
|
||||
struct impl {
|
||||
void incr() { ++value; }
|
||||
void set(int v) { value = v; }
|
||||
int get() const { return value; }
|
||||
int value{};
|
||||
};
|
||||
|
||||
TEST(Poly, Functionalities) {
|
||||
impl instance{};
|
||||
|
||||
entt::poly<concept> empty{};
|
||||
entt::poly<concept> in_place{std::in_place_type<impl>, 3};
|
||||
entt::poly<concept> alias{std::ref(instance)};
|
||||
entt::poly<concept> value{impl{}};
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
ASSERT_TRUE(in_place);
|
||||
ASSERT_TRUE(alias);
|
||||
ASSERT_TRUE(value);
|
||||
|
||||
ASSERT_EQ(empty.type(), entt::type_info{});
|
||||
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(alias.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(value.type(), entt::type_id<impl>());
|
||||
|
||||
ASSERT_EQ(alias.data(), &instance);
|
||||
ASSERT_EQ(std::as_const(alias).data(), &instance);
|
||||
|
||||
empty = impl{};
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_NE(empty.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(empty).data(), nullptr);
|
||||
ASSERT_EQ(empty.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(empty.get(), 0);
|
||||
|
||||
empty.emplace<impl>(3);
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_EQ(empty.get(), 3);
|
||||
|
||||
entt::poly<concept> ref = in_place.ref();
|
||||
|
||||
ASSERT_TRUE(ref);
|
||||
ASSERT_NE(ref.data(), nullptr);
|
||||
ASSERT_EQ(ref.data(), in_place.data());
|
||||
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
|
||||
ASSERT_EQ(ref.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(ref.get(), 3);
|
||||
|
||||
entt::poly<concept> null{};
|
||||
std::swap(empty, null);
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
|
||||
entt::poly<concept> copy = in_place;
|
||||
|
||||
ASSERT_TRUE(copy);
|
||||
ASSERT_EQ(copy.get(), 3);
|
||||
|
||||
entt::poly<concept> move = std::move(copy);
|
||||
|
||||
ASSERT_TRUE(move);
|
||||
ASSERT_FALSE(copy);
|
||||
ASSERT_EQ(move.get(), 3);
|
||||
}
|
||||
|
||||
TEST(Poly, Owned) {
|
||||
entt::poly<concept> poly{impl{}};
|
||||
auto *ptr = static_cast<impl *>(poly.data());
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(ptr->value, 0);
|
||||
ASSERT_EQ(poly.get(), 0);
|
||||
|
||||
poly.set(1);
|
||||
poly.incr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 2);
|
||||
ASSERT_EQ(poly.get(), 2);
|
||||
ASSERT_EQ(poly.mul(3), 6);
|
||||
|
||||
poly.decr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 1);
|
||||
ASSERT_EQ(poly.get(), 1);
|
||||
ASSERT_EQ(poly.mul(3), 3);
|
||||
}
|
||||
|
||||
TEST(Poly, Alias) {
|
||||
impl instance{};
|
||||
entt::poly<concept> poly{std::ref(instance)};
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(instance.value, 0);
|
||||
ASSERT_EQ(poly.get(), 0);
|
||||
|
||||
poly.set(1);
|
||||
poly.incr();
|
||||
|
||||
ASSERT_EQ(instance.value, 2);
|
||||
ASSERT_EQ(poly.get(), 2);
|
||||
ASSERT_EQ(poly.mul(3), 6);
|
||||
|
||||
poly.decr();
|
||||
|
||||
ASSERT_EQ(instance.value, 1);
|
||||
ASSERT_EQ(poly.get(), 1);
|
||||
ASSERT_EQ(poly.mul(3), 3);
|
||||
}
|
||||
148
test/entt/poly/poly_deduced.cpp
Normal file
148
test/entt/poly/poly_deduced.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/type_traits.hpp>
|
||||
#include <entt/poly/poly.hpp>
|
||||
|
||||
struct Deduced: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void incr() { entt::poly_call<0>(*this); }
|
||||
void set(int v) { entt::poly_call<1>(*this, v); }
|
||||
int get() const { return entt::poly_call<2>(*this); }
|
||||
void decr() { entt::poly_call<3>(*this); }
|
||||
int mul(int v) { return entt::poly_call<4>(*this, v); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct members {
|
||||
static void decr(Type &self) { self.set(self.get()-1); }
|
||||
static double mul(Type &self, double v) { return v * self.get(); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list<
|
||||
&Type::incr,
|
||||
&Type::set,
|
||||
&Type::get,
|
||||
&members<Type>::decr,
|
||||
&members<Type>::mul
|
||||
>;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
void incr() { ++value; }
|
||||
void set(int v) { value = v; }
|
||||
int get() const { return value; }
|
||||
void decrement() { --value; }
|
||||
double multiply(double v) { return v * value; }
|
||||
int value{};
|
||||
};
|
||||
|
||||
TEST(PolyDeduced, Functionalities) {
|
||||
impl instance{};
|
||||
|
||||
entt::poly<Deduced> empty{};
|
||||
entt::poly<Deduced> in_place{std::in_place_type<impl>, 3};
|
||||
entt::poly<Deduced> alias{std::ref(instance)};
|
||||
entt::poly<Deduced> value{impl{}};
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
ASSERT_TRUE(in_place);
|
||||
ASSERT_TRUE(alias);
|
||||
ASSERT_TRUE(value);
|
||||
|
||||
ASSERT_EQ(empty.type(), entt::type_info{});
|
||||
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(alias.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(value.type(), entt::type_id<impl>());
|
||||
|
||||
ASSERT_EQ(alias.data(), &instance);
|
||||
ASSERT_EQ(std::as_const(alias).data(), &instance);
|
||||
|
||||
empty = impl{};
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_NE(empty.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(empty).data(), nullptr);
|
||||
ASSERT_EQ(empty.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(empty->get(), 0);
|
||||
|
||||
empty.template emplace<impl>(3);
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_EQ(empty->get(), 3);
|
||||
|
||||
entt::poly<Deduced> ref = as_ref(in_place);
|
||||
|
||||
ASSERT_TRUE(ref);
|
||||
ASSERT_NE(ref.data(), nullptr);
|
||||
ASSERT_EQ(ref.data(), in_place.data());
|
||||
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
|
||||
ASSERT_EQ(ref.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(ref->get(), 3);
|
||||
|
||||
entt::poly<Deduced> null{};
|
||||
std::swap(empty, null);
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
|
||||
entt::poly<Deduced> copy = in_place;
|
||||
|
||||
ASSERT_TRUE(copy);
|
||||
ASSERT_EQ(copy->get(), 3);
|
||||
|
||||
entt::poly<Deduced> move = std::move(copy);
|
||||
|
||||
ASSERT_TRUE(move);
|
||||
ASSERT_FALSE(copy);
|
||||
ASSERT_EQ(move->get(), 3);
|
||||
}
|
||||
|
||||
TEST(PolyDeduced, Owned) {
|
||||
entt::poly<Deduced> poly{impl{}};
|
||||
auto *ptr = static_cast<impl *>(poly.data());
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(ptr->value, 0);
|
||||
ASSERT_EQ(poly->get(), 0);
|
||||
|
||||
poly->set(1);
|
||||
poly->incr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 2);
|
||||
ASSERT_EQ(poly->get(), 2);
|
||||
ASSERT_EQ(poly->mul(3), 6);
|
||||
|
||||
poly->decr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 1);
|
||||
ASSERT_EQ(poly->get(), 1);
|
||||
ASSERT_EQ(poly->mul(3), 3);
|
||||
}
|
||||
|
||||
TEST(PolyDeduced, Alias) {
|
||||
impl instance{};
|
||||
entt::poly<Deduced> poly{std::ref(instance)};
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(instance.value, 0);
|
||||
ASSERT_EQ(poly->get(), 0);
|
||||
|
||||
poly->set(1);
|
||||
poly->incr();
|
||||
|
||||
ASSERT_EQ(instance.value, 2);
|
||||
ASSERT_EQ(poly->get(), 2);
|
||||
ASSERT_EQ(poly->mul(3), 6);
|
||||
|
||||
poly->decr();
|
||||
|
||||
ASSERT_EQ(instance.value, 1);
|
||||
ASSERT_EQ(poly->get(), 1);
|
||||
ASSERT_EQ(poly->mul(3), 3);
|
||||
}
|
||||
154
test/entt/poly/poly_defined.cpp
Normal file
154
test/entt/poly/poly_defined.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/type_traits.hpp>
|
||||
#include <entt/poly/poly.hpp>
|
||||
|
||||
struct Defined: entt::type_list<
|
||||
void(),
|
||||
void(int),
|
||||
int() const,
|
||||
void(),
|
||||
int(int)
|
||||
> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void incr() { entt::poly_call<0>(*this); }
|
||||
void set(int v) { entt::poly_call<1>(*this, v); }
|
||||
int get() const { return entt::poly_call<2>(*this); }
|
||||
void decr() { entt::poly_call<3>(*this); }
|
||||
int mul(int v) { return entt::poly_call<4>(*this, v); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct members {
|
||||
static void decr(Type &self) { self.decrement(); }
|
||||
static double mul(Type &self, double v) { return self.multiply(v); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list<
|
||||
&Type::incr,
|
||||
&Type::set,
|
||||
&Type::get,
|
||||
&members<Type>::decr,
|
||||
&members<Type>::mul
|
||||
>;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
void incr() { ++value; }
|
||||
void set(int v) { value = v; }
|
||||
int get() const { return value; }
|
||||
void decrement() { --value; }
|
||||
double multiply(double v) { return v * value; }
|
||||
int value{};
|
||||
};
|
||||
|
||||
TEST(PolyDefined, Functionalities) {
|
||||
impl instance{};
|
||||
|
||||
entt::poly<Defined> empty{};
|
||||
entt::poly<Defined> in_place{std::in_place_type<impl>, 3};
|
||||
entt::poly<Defined> alias{std::ref(instance)};
|
||||
entt::poly<Defined> value{impl{}};
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
ASSERT_TRUE(in_place);
|
||||
ASSERT_TRUE(alias);
|
||||
ASSERT_TRUE(value);
|
||||
|
||||
ASSERT_EQ(empty.type(), entt::type_info{});
|
||||
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(alias.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(value.type(), entt::type_id<impl>());
|
||||
|
||||
ASSERT_EQ(alias.data(), &instance);
|
||||
ASSERT_EQ(std::as_const(alias).data(), &instance);
|
||||
|
||||
empty = impl{};
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_NE(empty.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(empty).data(), nullptr);
|
||||
ASSERT_EQ(empty.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(empty->get(), 0);
|
||||
|
||||
empty.template emplace<impl>(3);
|
||||
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_EQ(empty->get(), 3);
|
||||
|
||||
entt::poly<Defined> ref = as_ref(in_place);
|
||||
|
||||
ASSERT_TRUE(ref);
|
||||
ASSERT_NE(ref.data(), nullptr);
|
||||
ASSERT_EQ(ref.data(), in_place.data());
|
||||
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
|
||||
ASSERT_EQ(ref.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(ref->get(), 3);
|
||||
|
||||
entt::poly<Defined> null{};
|
||||
std::swap(empty, null);
|
||||
|
||||
ASSERT_FALSE(empty);
|
||||
|
||||
entt::poly<Defined> copy = in_place;
|
||||
|
||||
ASSERT_TRUE(copy);
|
||||
ASSERT_EQ(copy->get(), 3);
|
||||
|
||||
entt::poly<Defined> move = std::move(copy);
|
||||
|
||||
ASSERT_TRUE(move);
|
||||
ASSERT_FALSE(copy);
|
||||
ASSERT_EQ(move->get(), 3);
|
||||
}
|
||||
|
||||
TEST(PolyDefined, Owned) {
|
||||
entt::poly<Defined> poly{impl{}};
|
||||
auto *ptr = static_cast<impl *>(poly.data());
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(ptr->value, 0);
|
||||
ASSERT_EQ(poly->get(), 0);
|
||||
|
||||
poly->set(1);
|
||||
poly->incr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 2);
|
||||
ASSERT_EQ(poly->get(), 2);
|
||||
ASSERT_EQ(poly->mul(3), 6);
|
||||
|
||||
poly->decr();
|
||||
|
||||
ASSERT_EQ(ptr->value, 1);
|
||||
ASSERT_EQ(poly->get(), 1);
|
||||
ASSERT_EQ(poly->mul(3), 3);
|
||||
}
|
||||
|
||||
TEST(PolyDefined, Alias) {
|
||||
impl instance{};
|
||||
entt::poly<Defined> poly{std::ref(instance)};
|
||||
|
||||
ASSERT_TRUE(poly);
|
||||
ASSERT_NE(poly.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(poly).data(), nullptr);
|
||||
ASSERT_EQ(instance.value, 0);
|
||||
ASSERT_EQ(poly->get(), 0);
|
||||
|
||||
poly->set(1);
|
||||
poly->incr();
|
||||
|
||||
ASSERT_EQ(instance.value, 2);
|
||||
ASSERT_EQ(poly->get(), 2);
|
||||
ASSERT_EQ(poly->mul(3), 6);
|
||||
|
||||
poly->decr();
|
||||
|
||||
ASSERT_EQ(instance.value, 1);
|
||||
ASSERT_EQ(poly->get(), 1);
|
||||
ASSERT_EQ(poly->mul(3), 3);
|
||||
}
|
||||
Reference in New Issue
Block a user