*: view_pack class and tests
This commit is contained in:
4
TODO
4
TODO
@@ -10,7 +10,9 @@
|
||||
|
||||
WIP:
|
||||
* HP: write documentation for custom storages and views!!
|
||||
* use extended get to remove is_eto_eligible checks from views and groups.
|
||||
* view pack: doc and plain function as an alias for operator|
|
||||
* reverse iterators, rbegin and rend for the view pack
|
||||
* use extended get to remove is_eto_eligible checks from views and groups
|
||||
* factory invoke: add support for external member functions as static meta functions
|
||||
* pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
|
||||
* make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
|
||||
|
||||
@@ -349,10 +349,6 @@ public:
|
||||
* The returned iterator points to the first entity of the view. If the view
|
||||
* is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
@@ -366,10 +362,6 @@ public:
|
||||
* the view. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
@@ -382,10 +374,6 @@ public:
|
||||
* The returned iterator points to the first entity of the reversed view. If
|
||||
* the view is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @note
|
||||
* Iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed view.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const {
|
||||
@@ -400,10 +388,6 @@ public:
|
||||
* the reversed view. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed view.
|
||||
*/
|
||||
|
||||
332
src/entt/entity/view_pack.hpp
Normal file
332
src/entt/entity/view_pack.hpp
Normal file
@@ -0,0 +1,332 @@
|
||||
#ifndef ENTT_ENTITY_VIEW_PACK_HPP
|
||||
#define ENTT_ENTITY_VIEW_PACK_HPP
|
||||
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "fwd.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief View pack.
|
||||
*
|
||||
* The view pack allows users to combine multiple views into a single iterable
|
||||
* object, while also giving them full control over which view should lead the
|
||||
* iteration.<br/>
|
||||
* Its intended primary use is for custom storage and views, but it can also be
|
||||
* very convenient in everyday use.
|
||||
*
|
||||
* @tparam View Type of the leading view of the pack.
|
||||
* @tparam Other Types of all other views of the pack.
|
||||
*/
|
||||
template<typename View, typename... Other>
|
||||
class view_pack {
|
||||
using common_entity_type = std::common_type_t<typename View::entity_type, typename Other::entity_type...>;
|
||||
|
||||
class view_pack_iterator {
|
||||
friend class view_pack<View, Other...>;
|
||||
|
||||
using underlying_iterator = typename View::iterator;
|
||||
|
||||
view_pack_iterator(underlying_iterator from, underlying_iterator to, const std::tuple<View, Other...> &ref) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
last{to},
|
||||
pack{std::get<Other>(ref)...}
|
||||
{
|
||||
if(it != last && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
const auto entity = *it;
|
||||
return (std::get<Other>(pack).contains(entity) && ...);
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = typename std::iterator_traits<underlying_iterator>::difference_type;
|
||||
using value_type = decltype(*std::declval<underlying_iterator>());
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
view_pack_iterator & operator++() ENTT_NOEXCEPT {
|
||||
while(++it != last && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
view_pack_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
view_pack_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const view_pack_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const view_pack_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator it;
|
||||
const underlying_iterator last;
|
||||
const std::tuple<Other...> pack;
|
||||
};
|
||||
|
||||
class iterable_view_pack {
|
||||
friend class view_pack<View, Other...>;
|
||||
|
||||
using iterable_view = decltype(std::declval<View>().each());
|
||||
|
||||
class iterable_view_pack_iterator {
|
||||
friend class iterable_view_pack;
|
||||
|
||||
using underlying_iterator = typename iterable_view::iterator;
|
||||
|
||||
iterable_view_pack_iterator(underlying_iterator from, underlying_iterator to, const std::tuple<Other...> &ref) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
last{to},
|
||||
pack{ref}
|
||||
{
|
||||
if(it != last && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
const auto entity = std::get<0>(*it);
|
||||
return (std::get<Other>(pack).contains(entity) && ...);
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = typename std::iterator_traits<underlying_iterator>::difference_type;
|
||||
using value_type = decltype(std::tuple_cat(*std::declval<underlying_iterator>(), std::declval<Other>().get({})...));
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
iterable_view_pack_iterator & operator++() ENTT_NOEXCEPT {
|
||||
while(++it != last && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterable_view_pack_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterable_view_pack_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
const auto curr = *it;
|
||||
return std::tuple_cat(curr, std::get<Other>(pack).get(std::get<0>(curr))...);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const iterable_view_pack_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const iterable_view_pack_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator it;
|
||||
const underlying_iterator last;
|
||||
const std::tuple<Other...> pack;
|
||||
};
|
||||
|
||||
iterable_view_pack(const std::tuple<View, Other...> &ref)
|
||||
: iterable{std::get<View>(ref).each()},
|
||||
pack{std::get<Other>(ref)...}
|
||||
{}
|
||||
|
||||
public:
|
||||
using iterator = iterable_view_pack_iterator;
|
||||
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return { iterable.begin(), iterable.end(), pack };
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return { iterable.end(), iterable.end(), pack };
|
||||
}
|
||||
|
||||
private:
|
||||
iterable_view iterable;
|
||||
std::tuple<Other...> pack;
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = common_entity_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator = view_pack_iterator;
|
||||
|
||||
/**
|
||||
* @brief Constructs a pack from a bunch of views.
|
||||
* @param view A reference to the leading view for the pack.
|
||||
* @param other References to the other views to use to construct the pack.
|
||||
*/
|
||||
view_pack(const View &view, const Other &... other)
|
||||
: pack{view, other...}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the pack.
|
||||
*
|
||||
* The returned iterator points to the first entity of the pack. If the pack
|
||||
* is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Iterators stay true to the order imposed by the first view of the pack.
|
||||
*
|
||||
* @return An iterator to the first entity of the pack.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return { std::get<View>(pack).begin(), std::get<View>(pack).end(), pack };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the pack.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the pack. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Iterators stay true to the order imposed by the first view of the pack.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the pack.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return { std::get<View>(pack).end(), std::get<View>(pack).end(), pack };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Type &...);
|
||||
* void(Type &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(auto value: std::get<View>(pack).each()) {
|
||||
const auto entity = std::get<0>(value);
|
||||
|
||||
if((std::get<Other>(pack).contains(entity) && ...)) {
|
||||
std::apply(func, std::tuple_cat(value, std::get<Other>(pack).get(entity)...));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ the pack.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the pack.
|
||||
*/
|
||||
[[nodiscard]] iterable_view_pack each() const ENTT_NOEXCEPT {
|
||||
return pack;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a copy of the requested view from a pack.
|
||||
* @tparam Type Type of the view to return.
|
||||
* @return A copy of the requested view from the pack.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] operator Type() const ENTT_NOEXCEPT {
|
||||
return std::get<Type>(pack);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Appends a view to a pack.
|
||||
* @tparam Args View template arguments.
|
||||
* @param view A reference to a view to append to the pack.
|
||||
* @return The extended pack.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] auto operator|(const basic_view<Args...> &view) const {
|
||||
return std::make_from_tuple<view_pack<View, Other..., basic_view<Args...>>>(std::tuple_cat(pack, std::make_tuple(view)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Appends a pack and therefore all its views to another pack.
|
||||
* @tparam Pack Types of views of the pack to append.
|
||||
* @param other A reference to the pack to append.
|
||||
* @return The extended pack.
|
||||
*/
|
||||
template<typename... Pack>
|
||||
[[nodiscard]] auto operator|(const view_pack<Pack...> &other) const {
|
||||
return std::make_from_tuple<view_pack<View, Other..., Pack...>>(std::tuple_cat(pack, std::make_tuple(static_cast<Pack>(other)...)));
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<View, Other...> pack;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Combines two views in a pack.
|
||||
* @tparam Args Template arguments of the first view.
|
||||
* @tparam Other Template arguments of the second view.
|
||||
* @param lhs A reference to the first view with which to create the pack.
|
||||
* @param rhs A reference to the second view with which to create the pack.
|
||||
* @return A pack that combines the two views in a single iterable object.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] auto operator|(const basic_view<Args...> &lhs, const basic_view<Other...> &rhs) {
|
||||
return view_pack{lhs, rhs};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Combines a view with a pack.
|
||||
* @tparam Args View template arguments.
|
||||
* @tparam Pack Types of views of the pack.
|
||||
* @param view A reference to the view to combine with the pack.
|
||||
* @param pack A reference to the pack to combine with the view.
|
||||
* @return The extended pack.
|
||||
*/
|
||||
template<typename... Args, typename... Pack>
|
||||
[[nodiscard]] auto operator|(const basic_view<Args...> &view, const view_pack<Pack...> &pack) {
|
||||
return view_pack{view} | pack;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "entity/storage.hpp"
|
||||
#include "entity/utility.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "entity/view_pack.hpp"
|
||||
#include "locator/locator.hpp"
|
||||
#include "meta/container.hpp"
|
||||
#include "meta/ctx.hpp"
|
||||
|
||||
@@ -174,6 +174,7 @@ SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
|
||||
SETUP_BASIC_TEST(view entt/entity/view.cpp)
|
||||
SETUP_BASIC_TEST(view_pack entt/entity/view_pack.cpp)
|
||||
|
||||
# Test locator
|
||||
|
||||
|
||||
154
test/entt/entity/view_pack.cpp
Normal file
154
test/entt/entity/view_pack.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/view.hpp>
|
||||
#include <entt/entity/view_pack.hpp>
|
||||
|
||||
struct empty_type {};
|
||||
|
||||
TEST(ViewPack, Construction) {
|
||||
entt::registry registry;
|
||||
|
||||
const auto view1 = registry.view<int, const char>();
|
||||
const auto view2 = registry.view<empty_type>();
|
||||
const auto view3 = registry.view<double>();
|
||||
|
||||
static_assert(std::is_same_v<decltype(entt::view_pack{view1}), entt::view_pack<entt::view<entt::exclude_t<>, int, const char>>>);
|
||||
static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(view1 | view2 | view3)>);
|
||||
|
||||
static_assert(std::is_same_v<
|
||||
decltype(entt::view_pack{view1, view2, view3}),
|
||||
entt::view_pack<
|
||||
entt::view<entt::exclude_t<>, int, const char>,
|
||||
entt::view<entt::exclude_t<>, empty_type>,
|
||||
entt::view<entt::exclude_t<>, double>
|
||||
>
|
||||
>);
|
||||
|
||||
static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(entt::view_pack{view1} | view2 | view3)>);
|
||||
static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(view1 | entt::view_pack{view2} | view3)>);
|
||||
static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(view1 | view2 | entt::view_pack{view3})>);
|
||||
}
|
||||
|
||||
TEST(ViewPack, ShortestPool) {
|
||||
entt::registry registry;
|
||||
entt::entity entities[3];
|
||||
|
||||
registry.create(std::begin(entities), std::end(entities));
|
||||
|
||||
registry.insert<int>(std::begin(entities), std::end(entities));
|
||||
registry.insert<empty_type>(std::begin(entities), std::end(entities));
|
||||
registry.insert<char>(std::rbegin(entities), std::rend(entities) - 1u);
|
||||
|
||||
const auto tmp = registry.view<char>() | registry.view<empty_type>();
|
||||
const auto pack = tmp | registry.view<const int>();
|
||||
|
||||
{
|
||||
std::size_t next{};
|
||||
|
||||
for(const auto entt: pack) {
|
||||
ASSERT_EQ(entt::to_integral(entt), ++next);
|
||||
ASSERT_TRUE((registry.has<int, char>(entt)));
|
||||
}
|
||||
}
|
||||
|
||||
auto it = pack.begin();
|
||||
|
||||
ASSERT_EQ(*it++, entities[1u]);
|
||||
ASSERT_EQ(++it, pack.end());
|
||||
ASSERT_TRUE(it == pack.end());
|
||||
ASSERT_FALSE(it != pack.end());
|
||||
|
||||
pack.each([®istry, next = 0u](const auto entt, auto &&cv, auto &&iv) mutable {
|
||||
static_assert(std::is_same_v<decltype(entt), const entt::entity>);
|
||||
static_assert(std::is_same_v<decltype(cv), char &>);
|
||||
static_assert(std::is_same_v<decltype(iv), const int &>);
|
||||
ASSERT_EQ(entt::to_integral(entt), ++next);
|
||||
ASSERT_EQ(&cv, registry.try_get<char>(entt));
|
||||
ASSERT_EQ(&iv, registry.try_get<int>(entt));
|
||||
});
|
||||
|
||||
auto eit = pack.each().begin();
|
||||
|
||||
ASSERT_EQ(std::get<0>(*eit++), entities[1u]);
|
||||
static_assert(std::is_same_v<decltype(std::get<1>(*eit)), char &>);
|
||||
static_assert(std::is_same_v<decltype(std::get<2>(*eit)), const int &>);
|
||||
ASSERT_EQ(++eit, pack.each().end());
|
||||
ASSERT_TRUE(eit == pack.each().end());
|
||||
ASSERT_FALSE(eit != pack.each().end());
|
||||
|
||||
{
|
||||
std::size_t next{};
|
||||
|
||||
for(const auto [entt, cv, iv]: pack.each()) {
|
||||
static_assert(std::is_same_v<decltype(entt), const entt::entity>);
|
||||
static_assert(std::is_same_v<decltype(cv), char &>);
|
||||
static_assert(std::is_same_v<decltype(iv), const int &>);
|
||||
ASSERT_EQ(entt::to_integral(entt), ++next);
|
||||
ASSERT_EQ(&cv, registry.try_get<char>(entt));
|
||||
ASSERT_EQ(&iv, registry.try_get<int>(entt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ViewPack, LongestPool) {
|
||||
entt::registry registry;
|
||||
entt::entity entities[3];
|
||||
|
||||
registry.create(std::begin(entities), std::end(entities));
|
||||
|
||||
registry.insert<int>(std::begin(entities), std::end(entities));
|
||||
registry.insert<empty_type>(std::begin(entities), std::end(entities));
|
||||
registry.insert<char>(std::rbegin(entities), std::rend(entities) - 1u);
|
||||
|
||||
const auto pack = registry.view<int>() | registry.view<empty_type>() | registry.view<const char>();
|
||||
|
||||
{
|
||||
std::size_t next{2u};
|
||||
|
||||
for(const auto entt: pack) {
|
||||
ASSERT_EQ(entt::to_integral(entt), next--);
|
||||
ASSERT_TRUE((registry.has<int, char>(entt)));
|
||||
}
|
||||
}
|
||||
|
||||
auto it = pack.begin();
|
||||
|
||||
ASSERT_EQ(*it++, entities[2u]);
|
||||
ASSERT_EQ(++it, pack.end());
|
||||
ASSERT_TRUE(it == pack.end());
|
||||
ASSERT_FALSE(it != pack.end());
|
||||
|
||||
pack.each([®istry, next = 2u](const auto entt, auto &&iv, auto &&cv) mutable {
|
||||
static_assert(std::is_same_v<decltype(entt), const entt::entity>);
|
||||
static_assert(std::is_same_v<decltype(iv), int &>);
|
||||
static_assert(std::is_same_v<decltype(cv), const char &>);
|
||||
ASSERT_EQ(entt::to_integral(entt), next--);
|
||||
ASSERT_EQ(&iv, registry.try_get<int>(entt));
|
||||
ASSERT_EQ(&cv, registry.try_get<char>(entt));
|
||||
});
|
||||
|
||||
auto eit = pack.each().begin();
|
||||
|
||||
ASSERT_EQ(std::get<0>(*eit++), entities[2u]);
|
||||
static_assert(std::is_same_v<decltype(std::get<1>(*eit)), int &>);
|
||||
static_assert(std::is_same_v<decltype(std::get<2>(*eit)), const char &>);
|
||||
ASSERT_EQ(++eit, pack.each().end());
|
||||
ASSERT_TRUE(eit == pack.each().end());
|
||||
ASSERT_FALSE(eit != pack.each().end());
|
||||
|
||||
{
|
||||
std::size_t next{2u};
|
||||
|
||||
for(const auto [entt, iv, cv]: pack.each()) {
|
||||
static_assert(std::is_same_v<decltype(entt), const entt::entity>);
|
||||
static_assert(std::is_same_v<decltype(iv), int &>);
|
||||
static_assert(std::is_same_v<decltype(cv), const char &>);
|
||||
ASSERT_EQ(entt::to_integral(entt), next--);
|
||||
ASSERT_EQ(&iv, registry.try_get<int>(entt));
|
||||
ASSERT_EQ(&cv, registry.try_get<char>(entt));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user