Compare commits

..

24 Commits

Author SHA1 Message Date
Michele Caini
5d15a3d69f updated single include file 2021-04-13 08:46:28 +02:00
Michele Caini
94292872dc ready to cut v3.7.1 (general quality of life improvements, no bug fixes, no new feature) 2021-04-12 23:37:53 +02:00
Michele Caini
5c8a1e7d10 type traits: removed is_std_hashable[_v] 2021-04-12 23:37:46 +02:00
Michele Caini
e98d8426bb meta: treat T(*)[N] as non-pointer-like types 2021-04-12 15:59:35 +02:00
Michele Caini
77f80cecf9 test: re-enable tests left commented by mistake 2021-04-12 15:59:31 +02:00
Michele Caini
bce26a1499 meta: remove dependency on std::array 2021-04-12 15:59:28 +02:00
Michele Caini
453f1c6edc test: std::strcmp ... 🤦‍♂️ 2021-04-12 15:59:20 +02:00
Michele Caini
9f8a36f2c9 any/meta: cleanup/better traits usage 2021-04-12 15:58:59 +02:00
Michele Caini
e6a5945463 meta_any: suppress a warning from clang 2021-04-12 15:56:01 +02:00
Michele Caini
60393fbc5f meta_any: decays Type on storage construction to handle correctly const T[N] types (close #687) 2021-04-12 15:55:59 +02:00
Michele Caini
b9a925dbd4 test: avoid regressions with any and deduced const T[N] types 2021-04-12 15:55:57 +02:00
Michele Caini
5dbdb1bcb5 any: internal support for both aggreagates and non-aggregates (qol enhancement :) 2021-04-12 15:55:55 +02:00
Michele Caini
d55cefc086 meta_any: no redundant copies, better perf 2021-04-12 15:55:53 +02:00
Michele Caini
5380e6d98b any: no redundant copies, better perf 2021-04-12 15:55:49 +02:00
Michele Caini
45cc24e0b8 any: favor aggregate initialization upon construction 2021-04-12 15:55:45 +02:00
Michele Caini
76bf1791eb test: cleanup 2021-04-12 13:02:23 +02:00
Michele Caini
7e9a4c4b16 view: initialize all data members on construction 2021-04-12 12:52:22 +02:00
Michele Caini
75cd5f169f *: review deduction guides 2021-04-12 12:48:36 +02:00
Michele Caini
10636c82a2 view/group: review extended iterable mode (close #686) 2021-04-07 10:33:52 +02:00
Michele Caini
04a6729963 build_system: minor changes 2021-04-06 16:49:11 +02:00
Michele Caini
b5617398ad doc: fixed typo 2021-04-06 16:47:20 +02:00
Michele Caini
da14641ccb meta: reduce instantiations due to meta_prop 2021-04-06 16:47:14 +02:00
Michele Caini
3997bd2396 meta: minor changes 2021-04-06 16:47:03 +02:00
Michele Caini
c876350e05 updated single include file 2021-04-02 12:04:31 +02:00
27 changed files with 7914 additions and 5159 deletions

View File

@@ -20,13 +20,19 @@ jobs:
- uses: actions/checkout@v2
- name: Install g++-7
if: ${{ matrix.compiler == 'g++-7' }}
run: sudo apt install g++-7
run: |
sudo apt-get update
sudo apt-get install g++-7 -y
- name: Install g++-8
if: ${{ matrix.compiler == 'g++-8' }}
run: sudo apt install g++-8
run: |
sudo apt-get update
sudo apt-get install g++-8 -y
- name: Install clang-8
if: ${{ matrix.compiler == 'clang++-8' }}
run: sudo apt install clang-8
run: |
sudo apt-get update
sudo apt-get install clang-8 -y
- name: Compile tests
working-directory: build
env:

3
TODO
View File

@@ -7,7 +7,9 @@
* custom pools example (multi instance, tables, enable/disable, and so on...)
WIP:
* HP: see msvc only issue at https://godbolt.org/z/8KW9PhG4c
* HP: scheduler, use any (or poly?) instead of unique_ptr
* HP: any and the like: remove constructor that accepts reference wrapper, allow only in-place T& and entt::make_any<T>(...)
* HP: resource, forward the id to the loader from the cache and if constexpr the call to load, update doc and describe customization points
* HP: make it possible to create views of the type `view<T, T>`, add get by index and such, allow to register custom pools by name with the registry
* HP: add user data to type_info
@@ -22,7 +24,6 @@ WIP:
* HP: make runtime views use opaque storage and therefore return also elements.
* HP: add exclude-only views to combine with packs
* HP: entity-aware observer, add observer functions aside observer class
* HP: any and the like: remove constructor that accepts reference wrapper, allow only in-place T&?
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
* pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
* snapshot: support for range-based archives

View File

@@ -278,7 +278,7 @@ registry.emplace_or_replace<position>(entity, 0., 0.);
This is a slightly faster alternative for the following snippet:
```cpp
if(registry.has<velocity>(entity)) {
if(registry.all_of<velocity>(entity)) {
registry.replace<velocity>(entity, 0., 0.);
} else {
registry.emplace<velocity>(entity, 0., 0.);

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 7
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_PATCH 1
#endif

View File

@@ -48,117 +48,151 @@ class basic_any {
template<typename Type>
static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) {
if constexpr(!std::is_void_v<Type>) {
if constexpr(std::is_lvalue_reference_v<Type>) {
using base_type = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr(std::is_void_v<Type>) {
switch(op) {
case operation::COPY:
case operation::MOVE:
case operation::REF:
case operation::CREF:
as<basic_any>(to).vtable = from.vtable;
break;
default:
break;
}
} else if constexpr(std::is_lvalue_reference_v<Type>) {
using base_type = std::decay_t<Type>;
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<base_type>) {
as<basic_any>(to).template emplace<base_type>(*static_cast<const base_type *>(from.instance));
}
break;
case operation::MOVE:
as<basic_any>(to).instance = from.instance;
[[fallthrough]];
case operation::DTOR:
break;
case operation::COMP:
return compare<base_type>(from.instance, to) ? to : nullptr;
case operation::ADDR:
return std::is_const_v<std::remove_reference_t<Type>> ? nullptr : from.instance;
case operation::CADDR:
return from.instance;
case operation::REF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<Type>;
break;
case operation::CREF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<const base_type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<base_type>();
break;
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<base_type>) {
as<basic_any>(to) = *static_cast<const base_type *>(from.instance);
}
} else if constexpr(in_situ<Type>) {
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
#else
auto *instance = const_cast<Type *>(reinterpret_cast<const Type *>(&from.storage));
#endif
break;
case operation::MOVE:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = from.vtable;
[[fallthrough]];
case operation::DTOR:
break;
case operation::COMP:
return compare<base_type>(from.instance, to) ? to : nullptr;
case operation::ADDR:
return std::is_const_v<std::remove_reference_t<Type>> ? nullptr : from.instance;
case operation::CADDR:
return from.instance;
case operation::REF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<Type>;
break;
case operation::CREF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<const base_type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<base_type>();
break;
}
} else if constexpr(in_situ<Type>) {
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
#else
auto *instance = const_cast<Type *>(reinterpret_cast<const Type *>(&from.storage));
#endif
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
new (&as<basic_any>(to).storage) Type{std::as_const(*instance)};
as<basic_any>(to).vtable = from.vtable;
}
break;
case operation::MOVE:
new (&as<basic_any>(to).storage) Type{std::move(*instance)};
[[fallthrough]];
case operation::DTOR:
instance->~Type();
break;
case operation::COMP:
return compare<Type>(instance, to) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return instance;
case operation::REF:
as<basic_any>(to).instance = instance;
as<basic_any>(to).vtable = basic_vtable<Type &>;
break;
case operation::CREF:
as<basic_any>(to).instance = instance;
as<basic_any>(to).vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<Type>();
break;
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
new (&as<basic_any>(to).storage) Type{std::as_const(*instance)};
as<basic_any>(to).vtable = from.vtable;
}
} else {
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
as<basic_any>(to).instance = new Type{*static_cast<const Type *>(from.instance)};
as<basic_any>(to).vtable = from.vtable;
}
break;
case operation::MOVE:
as<basic_any>(to).instance = from.instance;
break;
case operation::DTOR:
if constexpr(std::is_array_v<Type>) {
delete[] static_cast<const Type *>(from.instance);
} else {
delete static_cast<const Type *>(from.instance);
}
break;
case operation::COMP:
return compare<Type>(from.instance, to) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return from.instance;
case operation::REF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<Type &>;
break;
case operation::CREF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<Type>();
break;
break;
case operation::MOVE:
new (&as<basic_any>(to).storage) Type{std::move(*instance)};
as<basic_any>(to).vtable = from.vtable;
break;
case operation::DTOR:
instance->~Type();
break;
case operation::COMP:
return compare<Type>(instance, to) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return instance;
case operation::REF:
as<basic_any>(to).instance = instance;
as<basic_any>(to).vtable = basic_vtable<Type &>;
break;
case operation::CREF:
as<basic_any>(to).instance = instance;
as<basic_any>(to).vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<Type>();
break;
}
} else {
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
as<basic_any>(to).instance = new Type{*static_cast<const Type *>(from.instance)};
as<basic_any>(to).vtable = from.vtable;
}
break;
case operation::MOVE:
as<basic_any>(to).instance = std::exchange(as<basic_any>(&from).instance, nullptr);
as<basic_any>(to).vtable = from.vtable;
break;
case operation::DTOR:
if constexpr(std::is_array_v<Type>) {
delete[] static_cast<const Type *>(from.instance);
} else {
delete static_cast<const Type *>(from.instance);
}
break;
case operation::COMP:
return compare<Type>(from.instance, to) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return from.instance;
case operation::REF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<Type &>;
break;
case operation::CREF:
as<basic_any>(to).instance = from.instance;
as<basic_any>(to).vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
as<type_info>(to) = type_id<Type>();
break;
}
}
return nullptr;
}
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&... args) {
if constexpr(!std::is_void_v<Type>) {
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<Type>) {
if constexpr(std::is_aggregate_v<Type>) {
new (&storage) Type{std::forward<Args>(args)...};
} else {
new (&storage) Type(std::forward<Args>(args)...);
}
} else {
if constexpr(std::is_aggregate_v<Type>) {
instance = new Type{std::forward<Args>(args)...};
} else {
instance = new Type(std::forward<Args>(args)...);
}
}
}
}
public:
/*! @brief Default constructor. */
basic_any() ENTT_NOEXCEPT
@@ -172,20 +206,11 @@ public:
* @param args Parameters to use to construct the instance.
*/
template<typename Type, typename... Args>
explicit basic_any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
explicit basic_any(std::in_place_type_t<Type>, Args &&... args)
: instance{},
vtable{&basic_vtable<Type>}
{
if constexpr(!std::is_void_v<Type>) {
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<Type>) {
new (&storage) Type(std::forward<Args>(args)...);
} else {
instance = new Type(std::forward<Args>(args)...);
}
}
initialize<Type>(std::forward<Args>(args)...);
}
/**
@@ -213,7 +238,7 @@ public:
* @param other The instance to copy from.
*/
basic_any(const basic_any &other)
: basic_any{}
: basic_any{std::in_place_type<void>}
{
other.vtable(operation::COPY, other, this);
}
@@ -223,10 +248,9 @@ public:
* @param other The instance to move from.
*/
basic_any(basic_any &&other) ENTT_NOEXCEPT
: basic_any{}
: basic_any{std::in_place_type<void>}
{
other.vtable(operation::MOVE, other, this);
vtable = std::exchange(other.vtable, &basic_vtable<void>);
}
/*! @brief Frees the internal storage, whatever it means. */
@@ -235,12 +259,49 @@ public:
}
/**
* @brief Assignment operator.
* @param other The instance to assign from.
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This any object.
*/
basic_any & operator=(basic_any other) {
swap(*this, other);
basic_any & operator=(const basic_any &other) {
vtable(operation::DTOR, *this, nullptr);
other.vtable(operation::COPY, other, this);
return *this;
}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This any object.
*/
basic_any & operator=(basic_any &&other) {
vtable(operation::DTOR, *this, nullptr);
other.vtable(operation::MOVE, other, this);
return *this;
}
/**
* @brief Value assignment operator.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type>
basic_any & operator=(std::reference_wrapper<Type> value) ENTT_NOEXCEPT {
emplace<Type &>(value.get());
return *this;
}
/**
* @brief Value assignment operator.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
@@ -249,7 +310,7 @@ public:
* @return The type of the contained object, if any.
*/
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
type_info info;
type_info info{};
vtable(operation::TYPE, *this, &info);
return info;
}
@@ -275,12 +336,13 @@ public:
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
*this = basic_any{std::in_place_type<Type>, std::forward<Args>(args)...};
std::exchange(vtable, &basic_vtable<Type>)(operation::DTOR, *this, nullptr);
initialize<Type>(std::forward<Args>(args)...);
}
/*! @brief Destroys contained object */
void reset() {
*this = basic_any{};
std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, *this, nullptr);
}
/**
@@ -300,19 +362,6 @@ public:
return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
}
/**
* @brief Swaps two any objects.
* @param lhs A valid any object.
* @param rhs A valid any object.
*/
friend void swap(basic_any &lhs, basic_any &rhs) {
basic_any tmp{};
lhs.vtable(operation::MOVE, lhs, &tmp);
rhs.vtable(operation::MOVE, rhs, &lhs);
lhs.vtable(operation::MOVE, tmp, &rhs);
std::swap(lhs.vtable, rhs.vtable);
}
/**
* @brief Aliasing constructor.
* @return An any that shares a reference to an unmanaged object.
@@ -370,7 +419,7 @@ Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
auto * const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance);
return static_cast<Type>(*instance);
}
@@ -380,7 +429,7 @@ Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
auto * const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance);
return static_cast<Type>(std::move(*instance));
}

View File

@@ -212,7 +212,7 @@ private:
* @param str Human-readable identifer.
*/
template<typename Char, std::size_t N>
basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT
basic_hashed_string(const Char (&str)[N])
-> basic_hashed_string<Char>;

View File

@@ -562,30 +562,6 @@ template<typename Type>
inline constexpr auto is_complete_v = is_complete<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is
* hashable, false otherwise.
* @tparam Type Potentially hashable type.
*/
template <typename Type, typename = void>
struct is_std_hashable: std::false_type {};
/*! @copydoc is_std_hashable */
template <typename Type>
struct is_std_hashable<Type, std::enable_if_t<std::is_convertible_v<decltype(std::declval<std::hash<Type>>()(std::declval<Type>())), std::size_t>>>
: std::true_type
{};
/**
* @brief Helper variable template.
* @tparam Type Potentially hashable type.
*/
template <typename Type>
inline constexpr auto is_std_hashable_v = is_std_hashable<Type>::value;
/**
* @brief Provides the member constant `value` to true if a given type is empty
* and the empty type optimization is enabled, false otherwise.

View File

@@ -60,7 +60,8 @@ struct overloaded: Func... {
* @tparam Func Types of function objects.
*/
template<class... Func>
overloaded(Func...) -> overloaded<Func...>;
overloaded(Func...)
-> overloaded<Func...>;
/**

View File

@@ -115,7 +115,7 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
private:
It it;
const std::tuple<storage_type<Get> *...> pools;
std::tuple<storage_type<Get> *...> pools;
};
iterable_group(basic_sparse_set<Entity> * const ref, const std::tuple<storage_type<Get> *...> &cpools)

View File

@@ -324,7 +324,8 @@ bool operator!=(const basic_handle<Type> &lhs, const basic_handle<Other> &rhs) E
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
basic_handle(basic_registry<Entity> &, Entity)
-> basic_handle<Entity>;
/**
@@ -332,7 +333,8 @@ basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
basic_handle(const basic_registry<Entity> &, Entity)
-> basic_handle<const Entity>;
}

View File

@@ -48,19 +48,18 @@ private:
/**
* @brief Deduction guide.
*
* It allows to deduce the constness of a registry directly from the instance
* provided to the constructor.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_view(basic_registry<Entity> &) ENTT_NOEXCEPT -> as_view<Entity>;
as_view(basic_registry<Entity> &) -> as_view<Entity>;
/*! @copydoc as_view */
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_view(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_view<const Entity>;
as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
/**
@@ -103,19 +102,18 @@ private:
/**
* @brief Deduction guide.
*
* It allows to deduce the constness of a registry directly from the instance
* provided to the constructor.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_group(basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<Entity>;
as_group(basic_registry<Entity> &) -> as_group<Entity>;
/*! @copydoc as_group */
/**
* @brief Deduction guide.
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
as_group(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<const Entity>;
as_group(const basic_registry<Entity> &) -> as_group<const Entity>;

View File

@@ -155,7 +155,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
class iterable_view_iterator final {
friend class iterable_view;
iterable_view_iterator(It from, const basic_view &parent) ENTT_NOEXCEPT
iterable_view_iterator(It from, const basic_view *parent) ENTT_NOEXCEPT
: it{from},
view{parent}
{}
@@ -177,7 +177,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
}
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
return std::tuple_cat(std::make_tuple(*it), view.get(*it));
return std::tuple_cat(std::make_tuple(*it), view->get(*it));
}
[[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
@@ -190,7 +190,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
private:
It it;
const basic_view view;
const basic_view *view;
};
iterable_view(const basic_view &parent)
@@ -202,19 +202,19 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
using reverse_iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>>;
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
return { view.begin(), view };
return { view.begin(), &view };
}
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
return { view.end(), view };
return { view.end(), &view };
}
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
return { view.rbegin(), view };
return { view.rbegin(), &view };
}
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
return { view.rend(), view };
return { view.rend(), &view };
}
private:
@@ -230,7 +230,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
[[nodiscard]] unchecked_type unchecked(const basic_sparse_set<Entity> *cpool) const {
std::size_t pos{};
unchecked_type other{};
(static_cast<void>(std::get<storage_type<Component> *>(pools) == cpool ? nullptr : (other[pos] = std::get<storage_type<Component> *>(pools), other[pos++])), ...);
(static_cast<void>(std::get<storage_type<Component> *>(pools) == cpool ? void() : void(other[pos++] = std::get<storage_type<Component> *>(pools))), ...);
return other;
}
@@ -681,7 +681,8 @@ public:
/*! @brief Default constructor to use to create empty, invalid views. */
basic_view() ENTT_NOEXCEPT
: pools{}
: pools{},
filter{}
{}
/**
@@ -689,7 +690,8 @@ public:
* @param ref The storage for the type to iterate.
*/
basic_view(storage_type &ref) ENTT_NOEXCEPT
: pools{&ref}
: pools{&ref},
filter{}
{}
/**
@@ -956,7 +958,7 @@ private:
* @param storage The storage for the types to iterate.
*/
template<typename... Storage>
basic_view(Storage &... storage) ENTT_NOEXCEPT
basic_view(Storage &... storage)
-> basic_view<std::common_type_t<typename Storage::entity_type...>, entt::exclude_t<>, constness_as_t<typename Storage::value_type, Storage>...>;

View File

@@ -110,8 +110,8 @@ private:
static internal::meta_prop_node node{
nullptr,
property,
property + 1u
property[0u],
property[1u]
};
entt::meta_any instance{std::forward<Key>(key)};
@@ -313,7 +313,7 @@ struct meta_factory<Type> {
template<auto Candidate, typename Policy = as_is_t>
auto ctor() ENTT_NOEXCEPT {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
static_assert(std::is_same_v<std::decay_t<typename descriptor::return_type>, Type>, "The function doesn't return an object of the required type");
auto * const type = internal::meta_info<Type>::resolve();
static internal::meta_ctor_node node{

View File

@@ -2,7 +2,6 @@
#define ENTT_META_META_HPP
#include <array>
#include <cstddef>
#include <functional>
#include <iterator>
@@ -164,8 +163,6 @@ class meta_any {
template<typename Type>
static void basic_vtable(const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] void *to) {
if constexpr(!std::is_void_v<Type>) {
using base_type = std::remove_const_t<std::remove_reference_t<Type>>;
switch(op) {
case operation::DTOR:
if constexpr(!std::is_lvalue_reference_v<Type>) {
@@ -176,39 +173,37 @@ class meta_any {
break;
case operation::REF:
case operation::CREF:
*static_cast<meta_any *>(to) = (op == operation::REF ? meta_any{std::ref(any_cast<Type &>(const_cast<any &>(from)))} : meta_any{std::cref(any_cast<const base_type &>(from))});
*static_cast<meta_any *>(to) = (op == operation::REF ? meta_any{std::ref(any_cast<Type &>(const_cast<any &>(from)))} : meta_any{std::cref(any_cast<const std::decay_t<Type> &>(from))});
break;
case operation::DEREF:
case operation::CDEREF:
if constexpr(is_meta_pointer_like_v<base_type>) {
// for some reason vs2017 doesn't compile when using alias declarations with pointer_traits to get the element type
using element_type = std::remove_const_t<typename std::pointer_traits<std::remove_const_t<std::remove_reference_t<Type>>>::element_type>;
if constexpr(is_meta_pointer_like_v<std::decay_t<Type>>) {
using element_type = std::remove_const_t<typename std::pointer_traits<std::decay_t<Type>>::element_type>;
if constexpr(std::is_function_v<element_type>) {
*static_cast<meta_any *>(to) = any_cast<base_type>(from);
*static_cast<meta_any *>(to) = any_cast<std::decay_t<Type>>(from);
} else if constexpr(!std::is_same_v<element_type, void>) {
// for some reason vs2017 doesn't compile when using alias declarations with adl_meta_pointer_like
using adl_meta_pointer_like_type = adl_meta_pointer_like<std::remove_const_t<std::remove_reference_t<Type>>>;
using adl_meta_pointer_like_type = adl_meta_pointer_like<std::decay_t<Type>>;
if constexpr(std::is_lvalue_reference_v<decltype(adl_meta_pointer_like_type::dereference(std::declval<const base_type &>()))>) {
auto &&obj = adl_meta_pointer_like_type::dereference(any_cast<const base_type &>(from));
if constexpr(std::is_lvalue_reference_v<decltype(adl_meta_pointer_like_type::dereference(std::declval<const std::decay_t<Type> &>()))>) {
auto &&obj = adl_meta_pointer_like_type::dereference(any_cast<const std::decay_t<Type> &>(from));
*static_cast<meta_any *>(to) = (op == operation::DEREF ? meta_any{std::ref(obj)} : meta_any{std::cref(obj)});
} else {
*static_cast<meta_any *>(to) = adl_meta_pointer_like_type::dereference(any_cast<const base_type &>(from));
*static_cast<meta_any *>(to) = adl_meta_pointer_like_type::dereference(any_cast<const std::decay_t<Type> &>(from));
}
}
}
break;
case operation::SEQ:
case operation::CSEQ:
if constexpr(is_complete_v<meta_sequence_container_traits<base_type>>) {
*static_cast<meta_sequence_container *>(to) = { std::in_place_type<base_type>, (op == operation::SEQ ? const_cast<any &>(from).as_ref() : from.as_ref()) };
if constexpr(is_complete_v<meta_sequence_container_traits<std::decay_t<Type>>>) {
*static_cast<meta_sequence_container *>(to) = { std::in_place_type<std::decay_t<Type>>, (op == operation::SEQ ? const_cast<any &>(from).as_ref() : from.as_ref()) };
}
break;
case operation::ASSOC:
case operation::CASSOC:
if constexpr(is_complete_v<meta_associative_container_traits<base_type>>) {
*static_cast<meta_associative_container *>(to) = { std::in_place_type<base_type>, (op == operation::ASSOC ? const_cast<any &>(from).as_ref() : from.as_ref()) };
if constexpr(is_complete_v<meta_associative_container_traits<std::decay_t<Type>>>) {
*static_cast<meta_associative_container *>(to) = { std::in_place_type<std::decay_t<Type>>, (op == operation::ASSOC ? const_cast<any &>(from).as_ref() : from.as_ref()) };
}
break;
}
@@ -231,7 +226,7 @@ public:
*/
template<typename Type, typename... Args>
explicit meta_any(std::in_place_type_t<Type>, Args &&... args)
: storage(std::in_place_type<Type>, std::forward<Args>(args)...),
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
node{internal::meta_info<Type>::resolve()},
vtable{&basic_vtable<Type>}
{}
@@ -251,9 +246,9 @@ public:
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
meta_any(Type &&value)
: meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
: meta_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)}
{}
/**
@@ -267,25 +262,62 @@ public:
* @param other The instance to move from.
*/
meta_any(meta_any &&other) ENTT_NOEXCEPT
: meta_any{}
{
swap(*this, other);
}
: storage{std::move(other.storage)},
node{std::exchange(other.node, nullptr)},
vtable{std::exchange(other.vtable, &basic_vtable<void>)}
{}
/*! @brief Frees the internal storage, whatever it means. */
~meta_any() {
if(vtable) {
vtable(operation::DTOR, storage, node);
}
vtable(operation::DTOR, storage, node);
}
/**
* @brief Assignment operator.
* @param other The instance to assign from.
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This meta any object.
*/
meta_any & operator=(meta_any other) {
swap(other, *this);
meta_any & operator=(const meta_any &other) {
std::exchange(vtable, other.vtable)(operation::DTOR, storage, node);
storage = other.storage;
node = other.node;
return *this;
}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This meta any object.
*/
meta_any & operator=(meta_any &&other) {
std::exchange(vtable, std::exchange(other.vtable, &basic_vtable<void>))(operation::DTOR, storage, node);
storage = std::move(other.storage);
node = std::exchange(other.node, nullptr);
return *this;
}
/**
* @brief Value assignment operator.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This meta any object.
*/
template<typename Type>
meta_any & operator=(std::reference_wrapper<Type> value) {
emplace<Type &>(value.get());
return *this;
}
/**
* @brief Value assignment operator.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This meta any object.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &>
operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
@@ -403,9 +435,9 @@ public:
template<typename Type>
[[nodiscard]] Type cast() {
// forces const on non-reference types to make them work also with wrappers for const references
auto * const actual = try_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>();
ENTT_ASSERT(actual);
return static_cast<Type>(*actual);
auto * const instance = try_cast<std::remove_reference_t<const Type>>();
ENTT_ASSERT(instance);
return static_cast<Type>(*instance);
}
/**
@@ -434,7 +466,8 @@ public:
*/
template<typename Type>
bool allow_cast() {
if(try_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>() != nullptr) {
// forces const on non-reference types to make them work also with wrappers for const references
if(try_cast<std::remove_reference_t<const Type>>() != nullptr) {
return true;
} else if(node) {
if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id<Type>()](const auto *curr) { return curr->type()->info == info; }, node); conv) {
@@ -454,12 +487,16 @@ public:
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
*this = meta_any{std::in_place_type<Type>, std::forward<Args>(args)...};
std::exchange(vtable, &basic_vtable<Type>)(operation::DTOR, storage, node);
storage.emplace<Type>(std::forward<Args>(args)...);
node = internal::meta_info<Type>::resolve();
}
/*! @brief Destroys contained object */
void reset() {
*this = meta_any{};
std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, storage, node);
storage.reset();
node = nullptr;
}
/**
@@ -531,18 +568,6 @@ public:
return (!node && !other.node) || (node && other.node && node->info == other.node->info && storage == other.storage);
}
/**
* @brief Swaps two meta any objects.
* @param lhs A valid meta any object.
* @param rhs A valid meta any object.
*/
friend void swap(meta_any &lhs, meta_any &rhs) {
using std::swap;
swap(lhs.storage, rhs.storage);
swap(lhs.vtable, rhs.vtable);
swap(lhs.node, rhs.node);
}
/**
* @brief Aliasing constructor.
* @return A meta any that shares a reference to an unmanaged object.
@@ -613,11 +638,11 @@ struct meta_handle {
* @tparam Type Type of object to use to initialize the handle.
* @param value An instance of an object to use to initialize the handle.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_handle>>>
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>>
meta_handle(Type &value) ENTT_NOEXCEPT
: meta_handle{}
{
if constexpr(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>) {
if constexpr(std::is_same_v<std::decay_t<Type>, meta_any>) {
any = value.as_ref();
} else {
any.emplace<Type &>(value);
@@ -668,7 +693,7 @@ struct meta_prop {
* @return A meta any containing the key stored with the property.
*/
[[nodiscard]] meta_any key() const {
return node->id->as_ref();
return node->id.as_ref();
}
/**
@@ -676,7 +701,7 @@ struct meta_prop {
* @return A meta any containing the value stored with the property.
*/
[[nodiscard]] meta_any value() const {
return *node->value;
return node->value;
}
/**
@@ -750,8 +775,8 @@ struct meta_ctor {
*/
template<typename... Args>
[[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const {
std::array<meta_any, sizeof...(Args)> arguments{std::forward<Args>(args)...};
return invoke(arguments.data(), sizeof...(Args));
meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
return invoke(arguments, sizeof...(Args));
}
/**
@@ -768,7 +793,7 @@ struct meta_ctor {
* @return The property associated with the given key, if any.
*/
[[nodiscard]] meta_prop prop(meta_any key) const {
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return *curr->id == key; }, node);
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node);
}
/**
@@ -864,7 +889,7 @@ struct meta_data {
* @return The property associated with the given key, if any.
*/
[[nodiscard]] meta_prop prop(meta_any key) const {
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return *curr->id == key; }, node);
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node);
}
/**
@@ -968,8 +993,8 @@ struct meta_func {
*/
template<typename... Args>
meta_any invoke(meta_handle instance, Args &&... args) const {
std::array<meta_any, sizeof...(Args)> arguments{std::forward<Args>(args)...};
return invoke(std::move(instance), arguments.data(), sizeof...(Args));
meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
return invoke(std::move(instance), arguments, sizeof...(Args));
}
/*! @copydoc meta_ctor::prop */
@@ -983,7 +1008,7 @@ struct meta_func {
* @return The property associated with the given key, if any.
*/
[[nodiscard]] meta_prop prop(meta_any key) const {
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return *curr->id == key; }, node);
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node);
}
/**
@@ -1373,8 +1398,8 @@ public:
*/
template<typename... Args>
[[nodiscard]] meta_any construct(Args &&... args) const {
std::array<meta_any, sizeof...(Args)> arguments{std::forward<Args>(args)...};
return construct(arguments.data(), sizeof...(Args));
meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
return construct(arguments, sizeof...(Args));
}
/**
@@ -1434,8 +1459,8 @@ public:
*/
template<typename... Args>
meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const {
std::array<meta_any, sizeof...(Args)> arguments{std::forward<Args>(args)...};
return invoke(id, std::move(instance), arguments.data(), sizeof...(Args));
meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...};
return invoke(id, std::move(instance), arguments, sizeof...(Args));
}
/**
@@ -1491,7 +1516,7 @@ public:
* @return The property associated with the given key, if any.
*/
[[nodiscard]] meta_prop prop(meta_any key) const {
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return *curr->id == key; }, node);
return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node);
}
/**

View File

@@ -2,7 +2,6 @@
#define ENTT_META_NODE_HPP
#include <array>
#include <cstddef>
#include <type_traits>
#include <utility>
@@ -36,8 +35,8 @@ struct meta_type_node;
struct meta_prop_node {
meta_prop_node * next;
const meta_any * const id;
meta_any * const value;
const meta_any &id;
meta_any &value;
};
@@ -234,7 +233,7 @@ public:
meta_template_descriptor(),
std::rank_v<Type>,
[](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence<std::rank_v<Type>>{}); },
&meta_node<std::remove_cv_t<std::remove_pointer_t<Type>>>::resolve,
&meta_node<std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<Type>>>>::resolve,
&meta_node<std::remove_cv_t<std::remove_reference_t<std::remove_extent_t<Type>>>>::resolve,
meta_default_constructor(&node),
meta_default_constructor(&node)
@@ -251,7 +250,8 @@ struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>> {};
template<typename... Args>
meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
return std::array<meta_type_node *, sizeof...(Args)>{{internal::meta_info<Args>::resolve()...}}[index];
meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_info<Args>::resolve()...};
return args[index + 1u];
}

View File

@@ -20,6 +20,17 @@ struct is_meta_pointer_like<Type *>
{};
/**
* @brief Partial specialization used to reject pointers to arrays.
* @tparam Type Type of elements of the array.
* @tparam N Number of elements of the array.
*/
template<typename Type, std::size_t N>
struct is_meta_pointer_like<Type(*)[N]>
: std::false_type
{};
/**
* @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta
* system.

View File

@@ -2,7 +2,6 @@
#define ENTT_META_UTILITY_HPP
#include <array>
#include <cstddef>
#include <functional>
#include <type_traits>

View File

@@ -335,7 +335,7 @@ template<typename Ret, typename... Args>
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Candidate>
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
delegate(connect_arg_t<Candidate>)
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
@@ -345,7 +345,7 @@ delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
* @tparam Type Type of class or type of payload.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type &&) ENTT_NOEXCEPT
delegate(connect_arg_t<Candidate>, Type &&)
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
@@ -355,7 +355,7 @@ delegate(connect_arg_t<Candidate>, Type &&) ENTT_NOEXCEPT
* @tparam Args Types of arguments of a function type.
*/
template<typename Ret, typename... Args>
delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT
delegate(Ret(*)(const void *, Args...), const void * = nullptr)
-> delegate<Ret(Args...)>;

View File

@@ -510,7 +510,8 @@ private:
* @tparam Args Types of arguments of a function type.
*/
template<typename Ret, typename... Args>
sink(sigh<Ret(Args...)> &) ENTT_NOEXCEPT -> sink<Ret(Args...)>;
sink(sigh<Ret(Args...)> &)
-> sink<Ret(Args...)>;
}

View File

@@ -1,27 +1,28 @@
#include <algorithm>
#include <string.h>
#include <unordered_map>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/any.hpp>
struct empty {
~empty() { ++counter; }
inline static int counter = 0;
};
struct fat {
fat(double v1, double v2, double v3, double v4)
: value{v1, v2, v3, v4}
{}
double value[4];
inline static int counter{0};
~fat() { ++counter; }
bool operator==(const fat &other) const {
return std::equal(std::begin(value), std::end(value), std::begin(other.value), std::end(other.value));
}
};
struct empty {
inline static int counter = 0;
~empty() { ++counter; }
inline static int counter{0};
double value[4];
};
struct not_comparable {
@@ -40,7 +41,14 @@ struct not_copyable {
struct alignas(64u) over_aligned {};
TEST(Any, SBO) {
struct Any: ::testing::Test {
void SetUp() override {
fat::counter = 0;
empty::counter = 0;
}
};
TEST_F(Any, SBO) {
entt::any any{'c'};
ASSERT_TRUE(any);
@@ -49,7 +57,7 @@ TEST(Any, SBO) {
ASSERT_EQ(entt::any_cast<char>(any), 'c');
}
TEST(Any, NoSBO) {
TEST_F(Any, NoSBO) {
fat instance{.1, .2, .3, .4};
entt::any any{instance};
@@ -59,7 +67,7 @@ TEST(Any, NoSBO) {
ASSERT_EQ(entt::any_cast<fat>(any), instance);
}
TEST(Any, Empty) {
TEST_F(Any, Empty) {
entt::any any{};
ASSERT_FALSE(any);
@@ -68,7 +76,7 @@ TEST(Any, Empty) {
ASSERT_EQ(any.data(), nullptr);
}
TEST(Any, SBOInPlaceTypeConstruction) {
TEST_F(Any, SBOInPlaceTypeConstruction) {
entt::any any{std::in_place_type<int>, 42};
ASSERT_TRUE(any);
@@ -84,7 +92,7 @@ TEST(Any, SBOInPlaceTypeConstruction) {
ASSERT_EQ(other.data(), any.data());
}
TEST(Any, SBOAsRefConstruction) {
TEST_F(Any, SBOAsRefConstruction) {
int value = 42;
entt::any any{std::ref(value)};
@@ -103,6 +111,12 @@ TEST(Any, SBOAsRefConstruction) {
ASSERT_EQ(any.data(), &value);
ASSERT_EQ(std::as_const(any).data(), &value);
any = std::ref(value);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::type_id<int>());
ASSERT_EQ(entt::any_cast<int>(&any), &value);
auto other = any.as_ref();
ASSERT_TRUE(other);
@@ -111,7 +125,7 @@ TEST(Any, SBOAsRefConstruction) {
ASSERT_EQ(other.data(), any.data());
}
TEST(Any, SBOAsConstRefConstruction) {
TEST_F(Any, SBOAsConstRefConstruction) {
int value = 42;
entt::any any{std::cref(value)};
@@ -130,6 +144,12 @@ TEST(Any, SBOAsConstRefConstruction) {
ASSERT_EQ(any.data(), nullptr);
ASSERT_EQ(std::as_const(any).data(), &value);
any = std::cref(value);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::type_id<int>());
ASSERT_EQ(entt::any_cast<const int>(&any), &value);
auto other = any.as_ref();
ASSERT_TRUE(other);
@@ -138,7 +158,7 @@ TEST(Any, SBOAsConstRefConstruction) {
ASSERT_EQ(other.data(), any.data());
}
TEST(Any, SBOCopyConstruction) {
TEST_F(Any, SBOCopyConstruction) {
entt::any any{42};
entt::any other{any};
@@ -150,7 +170,7 @@ TEST(Any, SBOCopyConstruction) {
ASSERT_EQ(entt::any_cast<int>(other), 42);
}
TEST(Any, SBOCopyAssignment) {
TEST_F(Any, SBOCopyAssignment) {
entt::any any{42};
entt::any other{3};
@@ -164,33 +184,35 @@ TEST(Any, SBOCopyAssignment) {
ASSERT_EQ(entt::any_cast<int>(other), 42);
}
TEST(Any, SBOMoveConstruction) {
TEST_F(Any, SBOMoveConstruction) {
entt::any any{42};
entt::any other{std::move(any)};
ASSERT_FALSE(any);
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_FALSE(any.type());
ASSERT_NE(any.data(), nullptr);
ASSERT_EQ(any.type(), entt::type_id<int>());
ASSERT_EQ(other.type(), entt::type_id<int>());
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
ASSERT_EQ(entt::any_cast<int>(other), 42);
}
TEST(Any, SBOMoveAssignment) {
TEST_F(Any, SBOMoveAssignment) {
entt::any any{42};
entt::any other{3};
other = std::move(any);
ASSERT_FALSE(any);
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_FALSE(any.type());
ASSERT_NE(any.data(), nullptr);
ASSERT_EQ(any.type(), entt::type_id<int>());
ASSERT_EQ(other.type(), entt::type_id<int>());
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
ASSERT_EQ(entt::any_cast<int>(other), 42);
}
TEST(Any, SBODirectAssignment) {
TEST_F(Any, SBODirectAssignment) {
entt::any any{};
any = 42;
@@ -200,7 +222,7 @@ TEST(Any, SBODirectAssignment) {
ASSERT_EQ(entt::any_cast<int>(any), 42);
}
TEST(Any, NoSBOInPlaceTypeConstruction) {
TEST_F(Any, NoSBOInPlaceTypeConstruction) {
fat instance{.1, .2, .3, .4};
entt::any any{std::in_place_type<fat>, instance};
@@ -217,7 +239,7 @@ TEST(Any, NoSBOInPlaceTypeConstruction) {
ASSERT_EQ(other.data(), any.data());
}
TEST(Any, NoSBOAsRefConstruction) {
TEST_F(Any, NoSBOAsRefConstruction) {
fat instance{.1, .2, .3, .4};
entt::any any{std::ref(instance)};
@@ -236,6 +258,12 @@ TEST(Any, NoSBOAsRefConstruction) {
ASSERT_EQ(any.data(), &instance);
ASSERT_EQ(std::as_const(any).data(), &instance);
any = std::ref(instance);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::type_id<fat>());
ASSERT_EQ(entt::any_cast<fat>(&any), &instance);
auto other = any.as_ref();
ASSERT_TRUE(other);
@@ -244,7 +272,7 @@ TEST(Any, NoSBOAsRefConstruction) {
ASSERT_EQ(other.data(), any.data());
}
TEST(Any, NoSBOAsConstRefConstruction) {
TEST_F(Any, NoSBOAsConstRefConstruction) {
fat instance{.1, .2, .3, .4};
entt::any any{std::cref(instance)};
@@ -263,6 +291,12 @@ TEST(Any, NoSBOAsConstRefConstruction) {
ASSERT_EQ(any.data(), nullptr);
ASSERT_EQ(std::as_const(any).data(), &instance);
any = std::cref(instance);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::type_id<fat>());
ASSERT_EQ(entt::any_cast<const fat>(&any), &instance);
auto other = any.as_ref();
ASSERT_TRUE(other);
@@ -271,7 +305,7 @@ TEST(Any, NoSBOAsConstRefConstruction) {
ASSERT_EQ(other.data(), any.data());
}
TEST(Any, NoSBOCopyConstruction) {
TEST_F(Any, NoSBOCopyConstruction) {
fat instance{.1, .2, .3, .4};
entt::any any{instance};
entt::any other{any};
@@ -284,7 +318,7 @@ TEST(Any, NoSBOCopyConstruction) {
ASSERT_EQ(entt::any_cast<fat>(other), instance);
}
TEST(Any, NoSBOCopyAssignment) {
TEST_F(Any, NoSBOCopyAssignment) {
fat instance{.1, .2, .3, .4};
entt::any any{instance};
entt::any other{3};
@@ -299,19 +333,21 @@ TEST(Any, NoSBOCopyAssignment) {
ASSERT_EQ(entt::any_cast<fat>(other), instance);
}
TEST(Any, NoSBOMoveConstruction) {
TEST_F(Any, NoSBOMoveConstruction) {
fat instance{.1, .2, .3, .4};
entt::any any{instance};
entt::any other{std::move(any)};
ASSERT_FALSE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.data(), nullptr);
ASSERT_EQ(any.type(), entt::type_id<fat>());
ASSERT_EQ(other.type(), entt::type_id<fat>());
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
ASSERT_EQ(entt::any_cast<fat>(other), instance);
}
TEST(Any, NoSBOMoveAssignment) {
TEST_F(Any, NoSBOMoveAssignment) {
fat instance{.1, .2, .3, .4};
entt::any any{instance};
entt::any other{3};
@@ -320,12 +356,14 @@ TEST(Any, NoSBOMoveAssignment) {
ASSERT_FALSE(any);
ASSERT_TRUE(other);
ASSERT_EQ(any.data(), nullptr);
ASSERT_EQ(any.type(), entt::type_id<fat>());
ASSERT_EQ(other.type(), entt::type_id<fat>());
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
ASSERT_EQ(entt::any_cast<fat>(other), instance);
}
TEST(Any, NoSBODirectAssignment) {
TEST_F(Any, NoSBODirectAssignment) {
fat instance{.1, .2, .3, .4};
entt::any any{};
any = instance;
@@ -336,7 +374,7 @@ TEST(Any, NoSBODirectAssignment) {
ASSERT_EQ(entt::any_cast<fat>(any), instance);
}
TEST(Any, VoidInPlaceTypeConstruction) {
TEST_F(Any, VoidInPlaceTypeConstruction) {
entt::any any{std::in_place_type<void>};
ASSERT_FALSE(any);
@@ -344,7 +382,7 @@ TEST(Any, VoidInPlaceTypeConstruction) {
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
}
TEST(Any, VoidCopyConstruction) {
TEST_F(Any, VoidCopyConstruction) {
entt::any any{std::in_place_type<void>};
entt::any other{any};
@@ -356,9 +394,9 @@ TEST(Any, VoidCopyConstruction) {
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
}
TEST(Any, VoidCopyAssignment) {
TEST_F(Any, VoidCopyAssignment) {
entt::any any{std::in_place_type<void>};
entt::any other{std::in_place_type<void>};
entt::any other{42};
other = any;
@@ -370,7 +408,7 @@ TEST(Any, VoidCopyAssignment) {
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
}
TEST(Any, VoidMoveConstruction) {
TEST_F(Any, VoidMoveConstruction) {
entt::any any{std::in_place_type<void>};
entt::any other{std::move(any)};
@@ -382,9 +420,9 @@ TEST(Any, VoidMoveConstruction) {
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
}
TEST(Any, VoidMoveAssignment) {
TEST_F(Any, VoidMoveAssignment) {
entt::any any{std::in_place_type<void>};
entt::any other{std::in_place_type<void>};
entt::any other{42};
other = std::move(any);
@@ -396,17 +434,17 @@ TEST(Any, VoidMoveAssignment) {
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
}
TEST(Any, SBOMoveInvalidate) {
TEST_F(Any, SBOMoveValidButUnspecifiedState) {
entt::any any{42};
entt::any other{std::move(any)};
entt::any valid = std::move(other);
ASSERT_FALSE(any);
ASSERT_FALSE(other);
ASSERT_TRUE(any);
ASSERT_TRUE(other);
ASSERT_TRUE(valid);
}
TEST(Any, NoSBOMoveInvalidate) {
TEST_F(Any, NoSBOMoveValidButUnspecifiedState) {
fat instance{.1, .2, .3, .4};
entt::any any{instance};
entt::any other{std::move(any)};
@@ -417,7 +455,7 @@ TEST(Any, NoSBOMoveInvalidate) {
ASSERT_TRUE(valid);
}
TEST(Any, VoidMoveInvalidate) {
TEST_F(Any, VoidMoveValidButUnspecifiedState) {
entt::any any{std::in_place_type<void>};
entt::any other{std::move(any)};
entt::any valid = std::move(other);
@@ -427,30 +465,36 @@ TEST(Any, VoidMoveInvalidate) {
ASSERT_FALSE(valid);
}
TEST(Any, SBODestruction) {
TEST_F(Any, SBODestruction) {
{
entt::any any{empty{}};
empty::counter = 0;
entt::any any{std::in_place_type<empty>};
any.emplace<empty>();
any = empty{};
entt::any other{std::move(any)};
any = std::move(other);
}
ASSERT_EQ(empty::counter, 1);
ASSERT_EQ(empty::counter, 6);
}
TEST(Any, NoSBODestruction) {
TEST_F(Any, NoSBODestruction) {
{
entt::any any{fat{1., 2., 3., 4.}};
fat::counter = 0;
entt::any any{std::in_place_type<fat>, 1., 2., 3., 4.};
any.emplace<fat>(1., 2., 3., 4.);
any = fat{1., 2., 3., 4.};
entt::any other{std::move(any)};
any = std::move(other);
}
ASSERT_EQ(fat::counter, 1);
ASSERT_EQ(fat::counter, 4);
}
TEST(Any, VoidDestruction) {
TEST_F(Any, VoidDestruction) {
// just let asan tell us if everything is ok here
[[maybe_unused]] entt::any any{std::in_place_type<void>};
}
TEST(Any, Emplace) {
TEST_F(Any, Emplace) {
entt::any any{};
any.emplace<int>(42);
@@ -460,7 +504,7 @@ TEST(Any, Emplace) {
ASSERT_EQ(entt::any_cast<int>(any), 42);
}
TEST(Any, EmplaceVoid) {
TEST_F(Any, EmplaceVoid) {
entt::any any{};
any.emplace<void>();
@@ -468,7 +512,7 @@ TEST(Any, EmplaceVoid) {
ASSERT_FALSE(any.type());
}
TEST(Any, Reset) {
TEST_F(Any, Reset) {
entt::any any{42};
ASSERT_TRUE(any);
@@ -480,7 +524,7 @@ TEST(Any, Reset) {
ASSERT_EQ(any.type(), entt::type_info{});
}
TEST(Any, SBOSwap) {
TEST_F(Any, SBOSwap) {
entt::any lhs{'c'};
entt::any rhs{42};
@@ -494,7 +538,7 @@ TEST(Any, SBOSwap) {
ASSERT_EQ(entt::any_cast<char>(rhs), 'c');
}
TEST(Any, NoSBOSwap) {
TEST_F(Any, NoSBOSwap) {
entt::any lhs{fat{.1, .2, .3, .4}};
entt::any rhs{fat{.4, .3, .2, .1}};
@@ -504,7 +548,7 @@ TEST(Any, NoSBOSwap) {
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{.1, .2, .3, .4}));
}
TEST(Any, VoidSwap) {
TEST_F(Any, VoidSwap) {
entt::any lhs{std::in_place_type<void>};
entt::any rhs{std::in_place_type<void>};
const auto *pre = lhs.data();
@@ -514,7 +558,7 @@ TEST(Any, VoidSwap) {
ASSERT_EQ(pre, lhs.data());
}
TEST(Any, SBOWithNoSBOSwap) {
TEST_F(Any, SBOWithNoSBOSwap) {
entt::any lhs{fat{.1, .2, .3, .4}};
entt::any rhs{'c'};
@@ -528,7 +572,7 @@ TEST(Any, SBOWithNoSBOSwap) {
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{.1, .2, .3, .4}));
}
TEST(Any, SBOWithRefSwap) {
TEST_F(Any, SBOWithRefSwap) {
int value = 3;
entt::any lhs{std::ref(value)};
entt::any rhs{'c'};
@@ -544,7 +588,7 @@ TEST(Any, SBOWithRefSwap) {
ASSERT_EQ(rhs.data(), &value);
}
TEST(Any, SBOWithConstRefSwap) {
TEST_F(Any, SBOWithConstRefSwap) {
int value = 3;
entt::any lhs{std::cref(value)};
entt::any rhs{'c'};
@@ -561,7 +605,7 @@ TEST(Any, SBOWithConstRefSwap) {
ASSERT_EQ(std::as_const(rhs).data(), &value);
}
TEST(Any, SBOWithEmptySwap) {
TEST_F(Any, SBOWithEmptySwap) {
entt::any lhs{'c'};
entt::any rhs{};
@@ -582,7 +626,7 @@ TEST(Any, SBOWithEmptySwap) {
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
}
TEST(Any, SBOWithVoidSwap) {
TEST_F(Any, SBOWithVoidSwap) {
entt::any lhs{'c'};
entt::any rhs{std::in_place_type<void>};
@@ -603,7 +647,7 @@ TEST(Any, SBOWithVoidSwap) {
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
}
TEST(Any, NoSBOWithRefSwap) {
TEST_F(Any, NoSBOWithRefSwap) {
int value = 3;
entt::any lhs{std::ref(value)};
entt::any rhs{fat{.1, .2, .3, .4}};
@@ -619,7 +663,7 @@ TEST(Any, NoSBOWithRefSwap) {
ASSERT_EQ(rhs.data(), &value);
}
TEST(Any, NoSBOWithConstRefSwap) {
TEST_F(Any, NoSBOWithConstRefSwap) {
int value = 3;
entt::any lhs{std::cref(value)};
entt::any rhs{fat{.1, .2, .3, .4}};
@@ -636,7 +680,7 @@ TEST(Any, NoSBOWithConstRefSwap) {
ASSERT_EQ(std::as_const(rhs).data(), &value);
}
TEST(Any, NoSBOWithEmptySwap) {
TEST_F(Any, NoSBOWithEmptySwap) {
entt::any lhs{fat{.1, .2, .3, .4}};
entt::any rhs{};
@@ -657,7 +701,7 @@ TEST(Any, NoSBOWithEmptySwap) {
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{.1, .2, .3, .4}));
}
TEST(Any, NoSBOWithVoidSwap) {
TEST_F(Any, NoSBOWithVoidSwap) {
entt::any lhs{fat{.1, .2, .3, .4}};
entt::any rhs{std::in_place_type<void>};
@@ -678,7 +722,7 @@ TEST(Any, NoSBOWithVoidSwap) {
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{.1, .2, .3, .4}));
}
TEST(Any, AsRef) {
TEST_F(Any, AsRef) {
entt::any any{42};
auto ref = any.as_ref();
auto cref = std::as_const(any).as_ref();
@@ -744,7 +788,7 @@ TEST(Any, AsRef) {
ASSERT_NE(entt::any_cast<int>(&cref), any.data());
}
TEST(Any, Comparable) {
TEST_F(Any, Comparable) {
auto test = [](entt::any any, entt::any other) {
ASSERT_EQ(any, any);
ASSERT_NE(other, any);
@@ -764,7 +808,7 @@ TEST(Any, Comparable) {
test(3, std::cref(value));
}
TEST(Any, NotComparable) {
TEST_F(Any, NotComparable) {
auto test = [](const auto &instance) {
entt::any any{std::cref(instance)};
@@ -782,7 +826,7 @@ TEST(Any, NotComparable) {
test(std::vector<not_comparable>{});
}
TEST(Any, CompareVoid) {
TEST_F(Any, CompareVoid) {
entt::any any{std::in_place_type<void>};
ASSERT_EQ(any, any);
@@ -797,7 +841,7 @@ TEST(Any, CompareVoid) {
ASSERT_FALSE(entt::any{} != any);
}
TEST(Any, AnyCast) {
TEST_F(Any, AnyCast) {
entt::any any{42};
const auto &cany = any;
@@ -813,7 +857,7 @@ TEST(Any, AnyCast) {
ASSERT_DEATH(entt::any_cast<double>(entt::any{42}), "");
}
TEST(Any, NotCopyableType) {
TEST_F(Any, NotCopyableType) {
auto test = [](entt::any any) {
entt::any copy{any};
@@ -830,7 +874,7 @@ TEST(Any, NotCopyableType) {
test(entt::any{std::in_place_type<not_copyable<4>>});
}
TEST(Any, Array) {
TEST_F(Any, Array) {
entt::any any{std::in_place_type<int[1]>};
entt::any copy{any};
@@ -847,7 +891,7 @@ TEST(Any, Array) {
ASSERT_EQ(entt::any_cast<const int(&)[1]>(std::as_const(any))[0], 42);
}
TEST(Any, CopyMoveReference) {
TEST_F(Any, CopyMoveReference) {
int value{};
auto test = [&](auto ref) {
@@ -857,7 +901,7 @@ TEST(Any, CopyMoveReference) {
entt::any move = std::move(any);
entt::any copy = move;
ASSERT_FALSE(any);
ASSERT_TRUE(any);
ASSERT_TRUE(move);
ASSERT_TRUE(copy);
@@ -880,7 +924,7 @@ TEST(Any, CopyMoveReference) {
test(std::cref(value));
}
TEST(Any, SBOVsZeroedSBOSize) {
TEST_F(Any, SBOVsZeroedSBOSize) {
entt::any sbo{42};
const auto *broken = sbo.data();
entt::any other = std::move(sbo);
@@ -894,7 +938,7 @@ TEST(Any, SBOVsZeroedSBOSize) {
ASSERT_EQ(valid, same.data());
}
TEST(Any, Alignment) {
TEST_F(Any, Alignment) {
static constexpr auto alignment = alignof(over_aligned);
auto test = [](auto *target, auto cb) {
@@ -917,3 +961,23 @@ TEST(Any, Alignment) {
entt::basic_any<alignment, alignment> sbo[2] = { over_aligned{}, over_aligned{} };
test(sbo, [](auto *pre, auto *post) { ASSERT_NE(pre, post); });
}
TEST_F(Any, AggregatesMustWork) {
struct aggregate_type { int value; };
// the goal of this test is to enforce the requirements for aggregate types
entt::any{std::in_place_type<aggregate_type>, 42}.emplace<aggregate_type>(42);
}
TEST_F(Any, DeducedArrayType) {
entt::any any{"array of char"};
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::type_id<const char *>());
ASSERT_EQ((strcmp("array of char", entt::any_cast<const char *>(any))), 0);
any = "another array of char";
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::type_id<const char *>());
ASSERT_EQ((strcmp("another array of char", entt::any_cast<const char *>(any))), 0);
}

View File

@@ -119,11 +119,6 @@ TEST(TypeTraits, IsComplete) {
static_assert(!entt::is_complete_v<void>);
}
TEST(TypeTraits, IsStdHashable) {
static_assert(entt::is_std_hashable_v<int>);
static_assert(!entt::is_std_hashable_v<not_comparable>);
}
TEST(TypeTraits, ConstnessAs) {
static_assert(std::is_same_v<entt::constness_as_t<int, char>, int>);
static_assert(std::is_same_v<entt::constness_as_t<const int, char>, int>);

View File

@@ -1,7 +1,7 @@
#include <utility>
#include <iterator>
#include <algorithm>
#include <iterator>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/entity/registry.hpp>
#include <entt/entity/group.hpp>
@@ -101,15 +101,6 @@ TEST(NonOwningGroup, Invalid) {
ASSERT_EQ(group.find(entity), group.end());
ASSERT_EQ(group.front(), entt::entity{entt::null});
ASSERT_EQ(group.back(), entt::entity{entt::null});
group.each([](const auto, const auto &) { FAIL(); });
group.each([](const auto &) { FAIL(); });
for([[maybe_unused]] auto all: group.each()) { FAIL(); }
for(auto first = group.each().rbegin(), last = group.each().rend(); first != last; ++first) { FAIL(); }
ASSERT_NO_FATAL_FAILURE(group.sort([](const auto, const auto) { FAIL(), true; }));
ASSERT_NO_FATAL_FAILURE(group.sort<const empty_type>());
}
TEST(NonOwningGroup, ElementAccess) {
@@ -168,6 +159,7 @@ TEST(NonOwningGroup, Empty) {
TEST(NonOwningGroup, Each) {
entt::registry registry;
auto group = registry.group(entt::get<int, char>);
auto iterable = group.each();
const auto e0 = registry.create();
registry.emplace<int>(e0, 0);
@@ -178,9 +170,10 @@ TEST(NonOwningGroup, Each) {
registry.emplace<char>(e1);
auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
auto citerable = cgroup.each();
std::size_t cnt = 0;
for(auto first = cgroup.each().rbegin(), last = cgroup.each().rend(); first != last; ++first) {
for(auto first = citerable.rbegin(), last = citerable.rend(); first != last; ++first) {
static_assert(std::is_same_v<decltype(*first), std::tuple<entt::entity, const int &, const char &>>);
ASSERT_EQ(std::get<1>(*first), cnt++);
}
@@ -193,7 +186,8 @@ TEST(NonOwningGroup, Each) {
cgroup.each([&cnt](const int &, const char &) { --cnt; });
cgroup.each([&cnt](auto, const int &, const char &) { --cnt; });
for(auto [entt, iv, cv]: group.each()) {
// do not use iterable, make sure an iterable group works when created from a temporary
for(auto [entt, iv, cv]: registry.group(entt::get<int, char>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
@@ -202,11 +196,11 @@ TEST(NonOwningGroup, Each) {
ASSERT_EQ(cnt, std::size_t{0});
auto it = group.each().begin();
auto rit = group.each().rbegin();
auto it = iterable.begin();
auto rit = iterable.rbegin();
ASSERT_EQ((it++, ++it), group.each().end());
ASSERT_EQ((rit++, ++rit), group.each().rend());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ((rit++, ++rit), iterable.rend());
}
TEST(NonOwningGroup, Sort) {
@@ -566,8 +560,9 @@ TEST(NonOwningGroup, EmptyTypes) {
ASSERT_EQ(entity, entt);
}
registry.group(entt::get<int, char, double>).each([](const auto, int, char, double) { FAIL(); });
ASSERT_EQ(registry.group(entt::get<int, char, double>).each().begin(), registry.group(entt::get<int, char, double>).each().end());
auto iterable = registry.group(entt::get<int, char, double>).each();
ASSERT_EQ(iterable.begin(), iterable.end());
}
TEST(NonOwningGroup, FrontBack) {
@@ -622,6 +617,20 @@ TEST(NonOwningGroup, ExtendedGet) {
ASSERT_EQ(std::get<1>(tup), 'c');
}
TEST(NonOwningGroup, IterableGroupAlgorithmCompatibility) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
const auto group = registry.group(entt::get<int, char>);
const auto iterable = group.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(OwningGroup, Functionalities) {
entt::registry registry;
auto group = registry.group<int>(entt::get<char>);
@@ -707,12 +716,6 @@ TEST(OwningGroup, Invalid) {
ASSERT_EQ(group.find(entity), group.end());
ASSERT_EQ(group.front(), entt::entity{entt::null});
ASSERT_EQ(group.back(), entt::entity{entt::null});
group.each([](const auto, const auto &) { FAIL(); });
group.each([](const auto &) { FAIL(); });
for([[maybe_unused]] auto all: group.each()) { FAIL(); }
for(auto first = group.each().rbegin(), last = group.each().rend(); first != last; ++first) { FAIL(); }
}
TEST(OwningGroup, ElementAccess) {
@@ -771,6 +774,7 @@ TEST(OwningGroup, Empty) {
TEST(OwningGroup, Each) {
entt::registry registry;
auto group = registry.group<int>(entt::get<char>);
auto iterable = group.each();
const auto e0 = registry.create();
registry.emplace<int>(e0, 0);
@@ -781,9 +785,10 @@ TEST(OwningGroup, Each) {
registry.emplace<char>(e1);
auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
auto citerable = cgroup.each();
std::size_t cnt = 0;
for(auto first = cgroup.each().rbegin(), last = cgroup.each().rend(); first != last; ++first) {
for(auto first = citerable.rbegin(), last = citerable.rend(); first != last; ++first) {
static_assert(std::is_same_v<decltype(*first), std::tuple<entt::entity, const int &, const char &>>);
ASSERT_EQ(std::get<1>(*first), cnt++);
}
@@ -796,7 +801,8 @@ TEST(OwningGroup, Each) {
cgroup.each([&cnt](const int &, const char &) { --cnt; });
cgroup.each([&cnt](auto, const int &, const char &) { --cnt; });
for(auto [entt, iv, cv]: group.each()) {
// do not use iterable, make sure an iterable group works when created from a temporary
for(auto [entt, iv, cv]: registry.group<int>(entt::get<char>).each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
@@ -805,11 +811,11 @@ TEST(OwningGroup, Each) {
ASSERT_EQ(cnt, std::size_t{0});
auto it = group.each().begin();
auto rit = group.each().rbegin();
auto it = iterable.begin();
auto rit = iterable.rbegin();
ASSERT_EQ((it++, ++it), group.each().end());
ASSERT_EQ((rit++, ++rit), group.each().rend());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ((rit++, ++rit), iterable.rend());
}
TEST(OwningGroup, SortOrdered) {
@@ -1259,8 +1265,9 @@ TEST(OwningGroup, EmptyTypes) {
ASSERT_EQ(entity, entt);
}
registry.group<double>(entt::get<int, char>).each([](const auto, double, int, char) { FAIL(); });
ASSERT_EQ(registry.group<double>(entt::get<int, char>).each().begin(), registry.group<double>(entt::get<int, char>).each().end());
auto iterable = registry.group<double>(entt::get<int, char>).each();
ASSERT_EQ(iterable.begin(), iterable.end());
}
TEST(OwningGroup, FrontBack) {
@@ -1362,3 +1369,17 @@ TEST(OwningGroup, ExtendedGet) {
ASSERT_EQ(std::get<0>(tup), 42);
ASSERT_EQ(std::get<1>(tup), 'c');
}
TEST(OwningGroup, IterableGroupAlgorithmCompatibility) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
const auto group = registry.group<int>(entt::get<char>);
const auto iterable = group.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}

View File

@@ -140,8 +140,6 @@ TEST(Organizer, EmplaceFreeFunctionWithPayload) {
entt::registry registry;
clazz instance;
// TODO
organizer.emplace<&clazz::ro_int_char_double>(instance, "t1");
organizer.emplace<&clazz::ro_int_with_payload>(instance, "t2");
organizer.emplace<&clazz::ro_char_with_payload, const clazz>(instance, "t3");

View File

@@ -1,3 +1,4 @@
#include <algorithm>
#include <tuple>
#include <utility>
#include <type_traits>
@@ -176,10 +177,14 @@ TEST(SingleComponentView, Each) {
registry.emplace<int>(registry.create(), 1);
auto view = registry.view<int>();
auto iterable = view.each();
auto cview = std::as_const(registry).view<const int>();
auto citerable = cview.each();
std::size_t cnt = 0;
for(auto first = cview.each().rbegin(), last = cview.each().rend(); first != last; ++first) {
for(auto first = citerable.rbegin(), last = citerable.rend(); first != last; ++first) {
static_assert(std::is_same_v<decltype(*first), std::tuple<entt::entity, const int &>>);
ASSERT_EQ(std::get<1>(*first), cnt++);
}
@@ -192,7 +197,8 @@ TEST(SingleComponentView, Each) {
cview.each([&cnt](const int &) { --cnt; });
cview.each([&cnt](auto, const int &) { --cnt; });
for(auto [entt, iv]: view.each()) {
// do not use iterable, make sure an iterable view works when created from a temporary
for(auto [entt, iv]: registry.view<int>().each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
ASSERT_EQ(iv, --cnt);
@@ -200,11 +206,11 @@ TEST(SingleComponentView, Each) {
ASSERT_EQ(cnt, std::size_t{0});
auto it = view.each().begin();
auto rit = view.each().rbegin();
auto it = iterable.begin();
auto rit = iterable.rbegin();
ASSERT_EQ((it++, ++it), view.each().end());
ASSERT_EQ((rit++, ++rit), view.each().rend());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ((rit++, ++rit), iterable.rend());
}
TEST(SingleComponentView, ConstNonConstAndAllInBetween) {
@@ -383,6 +389,19 @@ TEST(SingleComponentView, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(storage)}), entt::basic_view<entt::entity, entt::exclude_t<>, const int>>);
}
TEST(SingleComponentView, IterableViewAlgorithmCompatibility) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
const auto view = registry.view<int>();
const auto iterable = view.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(MultiComponentView, Functionalities) {
entt::registry registry;
auto view = registry.view<int, char>();
@@ -583,10 +602,14 @@ TEST(MultiComponentView, Each) {
registry.emplace<char>(e1);
auto view = registry.view<int, char>();
auto iterable = view.each();
auto cview = std::as_const(registry).view<const int, const char>();
auto citerable = cview.each();
std::size_t cnt = 0;
for(auto first = cview.each().rbegin(), last = cview.each().rend(); first != last; ++first) {
for(auto first = citerable.rbegin(), last = citerable.rend(); first != last; ++first) {
static_assert(std::is_same_v<decltype(*first), std::tuple<entt::entity, const int &, const char &>>);
ASSERT_EQ(std::get<1>(*first), cnt++);
}
@@ -599,7 +622,8 @@ TEST(MultiComponentView, Each) {
cview.each([&cnt](const int &, const char &) { --cnt; });
cview.each([&cnt](auto, const int &, const char &) { --cnt; });
for(auto [entt, iv, cv]: view.each()) {
// do not use iterable, make sure an iterable view works when created from a temporary
for(auto [entt, iv, cv]: registry.view<int, char>().each()) {
static_assert(std::is_same_v<decltype(entt), entt::entity>);
static_assert(std::is_same_v<decltype(iv), int &>);
static_assert(std::is_same_v<decltype(cv), char &>);
@@ -608,11 +632,11 @@ TEST(MultiComponentView, Each) {
ASSERT_EQ(cnt, std::size_t{0});
auto it = view.each().begin();
auto rit = view.each().rbegin();
auto it = iterable.begin();
auto rit = iterable.rbegin();
ASSERT_EQ((it++, ++it), view.each().end());
ASSERT_EQ((rit++, ++rit), view.each().rend());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ((rit++, ++rit), iterable.rend());
}
TEST(MultiComponentView, EachWithSuggestedType) {
@@ -925,6 +949,20 @@ TEST(MultiComponentView, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)}), entt::basic_view<entt::entity, entt::exclude_t<>, const int, const double>>);
}
TEST(MultiComponentView, IterableViewAlgorithmCompatibility) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.emplace<char>(entity);
const auto view = registry.view<int, char>();
const auto iterable = view.each();
const auto it = std::find_if(iterable.begin(), iterable.end(), [entity](auto args) { return std::get<0>(args) == entity; });
ASSERT_EQ(std::get<0>(*it), entity);
}
TEST(View, Pipe) {
entt::registry registry;
const auto entity = registry.create();

View File

@@ -1,3 +1,4 @@
#include <algorithm>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
@@ -15,28 +16,32 @@ struct clazz_t {
};
struct empty_t {
virtual ~empty_t() = default;
static void destroy(empty_t &) {
++counter;
virtual ~empty_t() {
++destructor_counter;
}
inline static int counter = 0;
static void destroy(empty_t &) {
++destroy_counter;
}
inline static int destroy_counter = 0;
inline static int destructor_counter = 0;
};
struct fat_t: empty_t {
fat_t(): foo{}, bar{}, gnam{} {}
fat_t()
: value{.0, .0, .0, .0}
{}
fat_t(int *value)
: foo{value}, bar{value}, gnam{}
fat_t(double v1, double v2, double v3, double v4)
: value{v1, v2, v3, v4}
{}
bool operator==(const fat_t &other) const {
return foo == other.foo && bar == other.bar;
return std::equal(std::begin(value), std::end(value), std::begin(other.value), std::end(other.value));
}
int *foo;
int *bar;
double gnam[4];
double value[4];
};
struct not_comparable_t {
@@ -74,7 +79,8 @@ struct MetaAny: ::testing::Test {
.func<&clazz_t::member>("member"_hs)
.func<&clazz_t::func>("func"_hs);
empty_t::counter = 0;
empty_t::destroy_counter = 0;
empty_t::destructor_counter = 0;
}
void TearDown() override {
@@ -96,8 +102,7 @@ TEST_F(MetaAny, SBO) {
}
TEST_F(MetaAny, NoSBO) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{instance};
ASSERT_TRUE(any);
@@ -141,38 +146,72 @@ TEST_F(MetaAny, SBOInPlaceTypeConstruction) {
TEST_F(MetaAny, SBOAsRefConstruction) {
int value = 3;
int other = 42;
int compare = 42;
entt::meta_any any{std::ref(value)};
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_FALSE(any.try_cast<std::size_t>());
ASSERT_EQ(any.cast<int &>(), 3);
ASSERT_EQ(any.cast<const int &>(), 3);
ASSERT_EQ(any.cast<int>(), 3);
ASSERT_NE(any.data(), nullptr);
ASSERT_NE(std::as_const(any).data(), nullptr);
ASSERT_EQ(any.data(), &value);
ASSERT_EQ(std::as_const(any).data(), &value);
ASSERT_EQ(any, (entt::meta_any{std::ref(value)}));
ASSERT_NE(any, (entt::meta_any{std::ref(other)}));
ASSERT_NE(any, (entt::meta_any{std::ref(compare)}));
ASSERT_NE(any, entt::meta_any{42});
ASSERT_EQ(entt::meta_any{3}, any);
any = std::ref(value);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(std::as_const(any).data(), &value);
auto other = any.as_ref();
ASSERT_TRUE(other);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 3);
ASSERT_EQ(other.data(), any.data());
}
TEST_F(MetaAny, SBOAsConstRefConstruction) {
int value = 3;
int other = 42;
int compare = 42;
entt::meta_any any{std::cref(value)};
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_FALSE(any.try_cast<std::size_t>());
ASSERT_DEATH(any.cast<int &>() = 3, "");
ASSERT_EQ(any.cast<const int &>(), 3);
ASSERT_EQ(any.cast<int>(), 3);
ASSERT_EQ(any.data(), nullptr);
ASSERT_NE(std::as_const(any).data(), nullptr);
ASSERT_EQ(std::as_const(any).data(), &value);
ASSERT_EQ(any, (entt::meta_any{std::cref(value)}));
ASSERT_NE(any, (entt::meta_any{std::cref(other)}));
ASSERT_NE(any, (entt::meta_any{std::cref(compare)}));
ASSERT_NE(any, entt::meta_any{42});
ASSERT_EQ(entt::meta_any{3}, any);
any = std::cref(value);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(std::as_const(any).data(), &value);
auto other = any.as_ref();
ASSERT_TRUE(other);
ASSERT_EQ(any.type(), entt::resolve<int>());
ASSERT_EQ(any.cast<int>(), 3);
ASSERT_EQ(other.data(), any.data());
}
TEST_F(MetaAny, SBOCopyConstruction) {
@@ -238,8 +277,7 @@ TEST_F(MetaAny, SBODirectAssignment) {
}
TEST_F(MetaAny, NoSBOInPlaceTypeConstruction) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{std::in_place_type<fat_t>, instance};
ASSERT_TRUE(any);
@@ -252,42 +290,73 @@ TEST_F(MetaAny, NoSBOInPlaceTypeConstruction) {
}
TEST_F(MetaAny, NoSBOAsRefConstruction) {
int value = 3;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{std::ref(instance)};
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<fat_t>());
ASSERT_FALSE(any.try_cast<std::size_t>());
ASSERT_EQ(any.cast<fat_t &>(), instance);
ASSERT_EQ(any.cast<const fat_t &>(), instance);
ASSERT_EQ(any.cast<fat_t>(), instance);
ASSERT_NE(any.data(), nullptr);
ASSERT_NE(std::as_const(any).data(), nullptr);
ASSERT_EQ(any.data(), &instance);
ASSERT_EQ(std::as_const(any).data(), &instance);
ASSERT_EQ(any, (entt::meta_any{std::ref(instance)}));
ASSERT_EQ(any, entt::meta_any{instance});
ASSERT_NE(entt::meta_any{fat_t{}}, any);
any = std::ref(instance);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<fat_t>());
ASSERT_EQ(std::as_const(any).data(), &instance);
auto other = any.as_ref();
ASSERT_TRUE(other);
ASSERT_EQ(any.type(), entt::resolve<fat_t>());
ASSERT_EQ(any, entt::meta_any{instance});
ASSERT_EQ(other.data(), any.data());
}
TEST_F(MetaAny, NoSBOAsConstRefConstruction) {
int value = 3;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{std::cref(instance)};
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<fat_t>());
ASSERT_FALSE(any.try_cast<std::size_t>());
ASSERT_DEATH(any.cast<fat_t &>() = {}, "");
ASSERT_EQ(any.cast<const fat_t &>(), instance);
ASSERT_EQ(any.cast<fat_t>(), instance);
ASSERT_EQ(any.data(), nullptr);
ASSERT_NE(std::as_const(any).data(), nullptr);
ASSERT_EQ(std::as_const(any).data(), &instance);
ASSERT_EQ(any, (entt::meta_any{std::cref(instance)}));
ASSERT_EQ(any, entt::meta_any{instance});
ASSERT_NE(entt::meta_any{fat_t{}}, any);
any = std::cref(instance);
ASSERT_TRUE(any);
ASSERT_EQ(any.type(), entt::resolve<fat_t>());
ASSERT_EQ(std::as_const(any).data(), &instance);
auto other = any.as_ref();
ASSERT_TRUE(other);
ASSERT_EQ(any.type(), entt::resolve<fat_t>());
ASSERT_EQ(any, entt::meta_any{instance});
ASSERT_EQ(other.data(), any.data());
}
TEST_F(MetaAny, NoSBOCopyConstruction) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{instance};
entt::meta_any other{any};
@@ -300,8 +369,7 @@ TEST_F(MetaAny, NoSBOCopyConstruction) {
}
TEST_F(MetaAny, NoSBOCopyAssignment) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{instance};
entt::meta_any other{3};
@@ -316,8 +384,7 @@ TEST_F(MetaAny, NoSBOCopyAssignment) {
}
TEST_F(MetaAny, NoSBOMoveConstruction) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{instance};
entt::meta_any other{std::move(any)};
@@ -330,8 +397,7 @@ TEST_F(MetaAny, NoSBOMoveConstruction) {
}
TEST_F(MetaAny, NoSBOMoveAssignment) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{instance};
entt::meta_any other{3};
@@ -346,13 +412,13 @@ TEST_F(MetaAny, NoSBOMoveAssignment) {
}
TEST_F(MetaAny, NoSBODirectAssignment) {
int value = 42;
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{};
any = fat_t{&value};
any = instance;
ASSERT_FALSE(any.try_cast<std::size_t>());
ASSERT_EQ(any.cast<fat_t>(), fat_t{&value});
ASSERT_EQ(any, entt::meta_any{fat_t{&value}});
ASSERT_EQ(any.cast<fat_t>(), instance);
ASSERT_EQ(any, (entt::meta_any{fat_t{.1, .2, .3, .4}}));
ASSERT_NE(fat_t{}, any);
}
@@ -422,8 +488,7 @@ TEST_F(MetaAny, SBOMoveInvalidate) {
}
TEST_F(MetaAny, NoSBOMoveInvalidate) {
int value = 42;
fat_t instance{&value};
fat_t instance{.1, .2, .3, .4};
entt::meta_any any{instance};
entt::meta_any other{std::move(any)};
entt::meta_any valid = std::move(other);
@@ -444,15 +509,29 @@ TEST_F(MetaAny, VoidMoveInvalidate) {
}
TEST_F(MetaAny, SBODestruction) {
ASSERT_EQ(empty_t::counter, 0);
{ entt::meta_any any{empty_t{}}; }
ASSERT_EQ(empty_t::counter, 1);
{
entt::meta_any any{std::in_place_type<empty_t>};
any.emplace<empty_t>();
any = empty_t{};
entt::meta_any other{std::move(any)};
any = std::move(other);
}
ASSERT_EQ(empty_t::destroy_counter, 3);
ASSERT_EQ(empty_t::destructor_counter,6);
}
TEST_F(MetaAny, NoSBODestruction) {
ASSERT_EQ(fat_t::counter, 0);
{ entt::meta_any any{fat_t{}}; }
ASSERT_EQ(fat_t::counter, 1);
{
entt::meta_any any{std::in_place_type<fat_t>, 1., 2., 3., 4.};
any.emplace<fat_t>(1., 2., 3., 4.);
any = fat_t{1., 2., 3., 4.};
entt::meta_any other{std::move(any)};
any = std::move(other);
}
ASSERT_EQ(fat_t::destroy_counter, 3);
ASSERT_EQ(empty_t::destructor_counter, 4);
}
TEST_F(MetaAny, VoidDestruction) {
@@ -508,14 +587,13 @@ TEST_F(MetaAny, SBOSwap) {
}
TEST_F(MetaAny, NoSBOSwap) {
int i{}, j{};
entt::meta_any lhs{fat_t{&i}};
entt::meta_any rhs{fat_t{&j}};
entt::meta_any lhs{fat_t{.1, .2, .3, .4}};
entt::meta_any rhs{fat_t{.4, .3, .2, .1}};
std::swap(lhs, rhs);
ASSERT_EQ(lhs.cast<fat_t>().foo, &j);
ASSERT_EQ(rhs.cast<fat_t>().bar, &i);
ASSERT_EQ(lhs.cast<fat_t>(), (fat_t{.4, .3, .2, .1}));
ASSERT_EQ(rhs.cast<fat_t>(), (fat_t{.1, .2, .3, .4}));
}
TEST_F(MetaAny, VoidSwap) {
@@ -529,8 +607,7 @@ TEST_F(MetaAny, VoidSwap) {
}
TEST_F(MetaAny, SBOWithNoSBOSwap) {
int value = 42;
entt::meta_any lhs{fat_t{&value}};
entt::meta_any lhs{fat_t{.1, .2, .3, .4}};
entt::meta_any rhs{'c'};
std::swap(lhs, rhs);
@@ -538,8 +615,7 @@ TEST_F(MetaAny, SBOWithNoSBOSwap) {
ASSERT_FALSE(lhs.try_cast<fat_t>());
ASSERT_EQ(lhs.cast<char>(), 'c');
ASSERT_FALSE(rhs.try_cast<char>());
ASSERT_EQ(rhs.cast<fat_t>().foo, &value);
ASSERT_EQ(rhs.cast<fat_t>().bar, &value);
ASSERT_EQ(rhs.cast<fat_t>(), (fat_t{.1, .2, .3, .4}));
}
TEST_F(MetaAny, SBOWithEmptySwap) {
@@ -568,31 +644,33 @@ TEST_F(MetaAny, SBOWithVoidSwap) {
}
TEST_F(MetaAny, NoSBOWithEmptySwap) {
int i{};
entt::meta_any lhs{fat_t{&i}};
entt::meta_any lhs{fat_t{.1, .2, .3, .4}};
entt::meta_any rhs{};
std::swap(lhs, rhs);
ASSERT_EQ(rhs.cast<fat_t>().bar, &i);
ASSERT_FALSE(lhs);
ASSERT_EQ(rhs.cast<fat_t>(), (fat_t{.1, .2, .3, .4}));
std::swap(lhs, rhs);
ASSERT_EQ(lhs.cast<fat_t>().bar, &i);
ASSERT_FALSE(rhs);
ASSERT_EQ(lhs.cast<fat_t>(), (fat_t{.1, .2, .3, .4}));
}
TEST_F(MetaAny, NoSBOWithVoidSwap) {
int i{};
entt::meta_any lhs{fat_t{&i}};
entt::meta_any lhs{fat_t{.1, .2, .3, .4}};
entt::meta_any rhs{std::in_place_type<void>};
std::swap(lhs, rhs);
ASSERT_EQ(rhs.cast<fat_t>().bar, &i);
ASSERT_EQ(lhs.type(), entt::resolve<void>());
ASSERT_EQ(rhs.cast<fat_t>(), (fat_t{.1, .2, .3, .4}));
std::swap(lhs, rhs);
ASSERT_EQ(lhs.cast<fat_t>().bar, &i);
ASSERT_EQ(rhs.type(), entt::resolve<void>());
ASSERT_EQ(lhs.cast<fat_t>(), (fat_t{.1, .2, .3, .4}));
}
TEST_F(MetaAny, AsRef) {
@@ -733,11 +811,11 @@ TEST_F(MetaAny, Cast) {
ASSERT_EQ(any.cast<fat_t &>(), fat_t{});
ASSERT_EQ(any.cast<fat_t>(), fat_t{});
ASSERT_EQ(any.cast<fat_t>().gnam[0u], 0.);
ASSERT_EQ(any.cast<fat_t>().value[0u], 0.);
any.cast<fat_t &>().gnam[0u] = 3.;
any.cast<fat_t &>().value[0u] = 3.;
ASSERT_EQ(any.cast<fat_t>().gnam[0u], 3.);
ASSERT_EQ(any.cast<fat_t>().value[0u], 3.);
}
TEST_F(MetaAny, Convert) {

View File

@@ -1,3 +1,4 @@
#include <string.h>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
@@ -18,7 +19,8 @@ struct MetaProp: ::testing::Test {
entt::meta<base_2_t>()
.type("base_2"_hs)
.prop("bool"_hs, false);
.prop("bool"_hs, false)
.prop("char[]"_hs, "char[]");
entt::meta<derived_t>()
.type("derived"_hs)
@@ -57,6 +59,17 @@ TEST_F(MetaProp, FromBase) {
ASSERT_EQ(prop_int.value().cast<int>(), 42);
}
TEST_F(MetaProp, DeducedArrayType) {
using namespace entt::literals;
auto prop = entt::resolve<base_2_t>().prop("char[]"_hs);
ASSERT_TRUE(prop);
ASSERT_EQ(prop.key(), "char[]"_hs);
ASSERT_EQ(prop.value().type(), entt::resolve<const char *>());
ASSERT_EQ(strcmp(prop.value().cast<const char *>(), "char[]"), 0);
}
TEST_F(MetaProp, ReRegistration) {
using namespace entt::literals;