core/entity/meta/poly:

* updated poly (static vtable, design review)
* .ref -> as_ref (any, meta_any, poly)
* poly storage
This commit is contained in:
Michele Caini
2020-12-12 15:09:29 +01:00
parent 2d17ef5c7e
commit a974235d10
14 changed files with 702 additions and 317 deletions

View File

@@ -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; };

View 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

View File

@@ -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());
}
}
}

View File

@@ -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:
*

View File

@@ -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"

View File

@@ -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));
}

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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>());

View 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());
}

View File

@@ -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);
}

View File

@@ -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);
}

View 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);
}

View 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);
}