From 61ef5a44e0f0240029c29f9242cf608a96c3db33 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Thu, 14 Jul 2022 08:45:59 +0200 Subject: [PATCH] meta: * improved sequence container support for non-contiguous containers * added support to (standard) deque as sequence containers --- TODO | 1 - src/entt/meta/container.hpp | 27 +++++++++++---- src/entt/meta/meta.hpp | 55 ++++++++++++++++++++----------- test/entt/meta/meta_container.cpp | 48 +++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 28 deletions(-) diff --git a/TODO b/TODO index 0188903a9..9e8537a9c 100644 --- a/TODO +++ b/TODO @@ -12,7 +12,6 @@ DOC: * update entity doc when the storage based model is in place WIP: -* meta: add deque support as sequence container * no gh-pages, use main/docs or similar, see settings/pages on gh * sparse set/storage support for move-only types, internal rework required * get rid of observers, storage based views made them pointless - document alternatives diff --git a/src/entt/meta/container.hpp b/src/entt/meta/container.hpp index dc63d8c8a..940219c48 100644 --- a/src/entt/meta/container.hpp +++ b/src/entt/meta/container.hpp @@ -2,6 +2,7 @@ #define ENTT_META_CONTAINER_HPP #include +#include #include #include #include @@ -58,26 +59,29 @@ struct basic_meta_sequence_container_traits { [[nodiscard]] static iterator iter(any &container, const bool as_end) { if(auto *const cont = any_cast(&container); cont) { - return iterator{*cont, static_cast(as_end * cont->size())}; + return iterator{as_end ? cont->end() : cont->begin()}; } const Type &as_const = any_cast(container); - return iterator{as_const, static_cast(as_end * as_const.size())}; + return iterator{as_end ? as_const.end() : as_const.begin()}; } - [[nodiscard]] static iterator insert_or_erase([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset, [[maybe_unused]] meta_any &value) { + [[nodiscard]] static iterator insert_or_erase([[maybe_unused]] any &container, [[maybe_unused]] const any &handle, [[maybe_unused]] meta_any &value) { if constexpr(is_dynamic_sequence_container::value) { if(auto *const cont = any_cast(&container); cont) { if(value) { // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector if(value.allow_cast() || value.allow_cast()) { const auto *element = value.try_cast>(); - const auto curr = cont->insert(std::next(cont->begin(), offset), element ? *element : value.cast()); - return iterator{*cont, static_cast(offset)}; + auto *it = any_cast(&handle); + + return iterator{cont->insert( + it ? *it : any_cast(handle), + element ? *element : value.cast())}; } } else { - const auto curr = cont->erase(std::next(cont->begin(), offset)); - return iterator{*cont, static_cast(offset)}; + auto *it = any_cast(&handle); + return iterator{it ? cont->erase(*it) : cont->erase(any_cast(handle))}; } } } @@ -178,6 +182,15 @@ template struct meta_sequence_container_traits> : internal::basic_meta_sequence_container_traits> {}; +/** + * @brief Meta sequence container traits for `std::deque`s of any type. + * @tparam Type The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_sequence_container_traits> + : internal::basic_meta_sequence_container_traits> {}; + /** * @brief Meta associative container traits for `std::map`s of any type. * @tparam Key The key type of elements. diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index ed2bb3825..c53a40cee 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -68,7 +68,7 @@ private: size_type (*size_fn)(const any &) noexcept = nullptr; bool (*resize_fn)(any &, size_type) = nullptr; iterator (*iter_fn)(any &, const bool) = nullptr; - iterator (*insert_or_erase_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; + iterator (*insert_or_erase_fn)(any &, const any &, meta_any &) = nullptr; any storage{}; }; @@ -1467,11 +1467,25 @@ inline bool meta_any::assign(meta_any &&other) { class meta_sequence_container::meta_iterator final { friend class meta_sequence_container; - using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + enum class operation : std::uint8_t { + incr, + deref + }; + + using vtable_type = void(const operation, const any &, const std::ptrdiff_t, meta_any *); template - static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { - return meta_any{std::in_place_type::reference>, *std::next(any_cast(value), pos)}; + static void basic_vtable(const operation op, const any &value, const std::ptrdiff_t offset, meta_any *other) { + switch(op) { + case operation::incr: { + auto &it = any_cast(const_cast(value)); + it = std::next(it, offset); + } break; + case operation::deref: { + const auto &it = any_cast(value); + other->emplace(*it); + } break; + } } public: @@ -1482,38 +1496,40 @@ public: using iterator_category = std::input_iterator_tag; constexpr meta_iterator() noexcept - : deref{}, - offset{}, + : vtable{}, handle{} {} - template - explicit meta_iterator(Type &cont, const difference_type init) noexcept - : deref{&deref_fn}, - offset{init}, - handle{cont.begin()} {} + template + explicit meta_iterator(It iter) noexcept + : vtable{&basic_vtable}, + handle{std::move(iter)} {} meta_iterator &operator++() noexcept { - return ++offset, *this; + vtable(operation::incr, handle, 1, nullptr); + return *this; } meta_iterator operator++(int value) noexcept { meta_iterator orig = *this; - offset += ++value; + vtable(operation::incr, handle, ++value, nullptr); return orig; } meta_iterator &operator--() noexcept { - return --offset, *this; + vtable(operation::incr, handle, -1, nullptr); + return *this; } meta_iterator operator--(int value) noexcept { meta_iterator orig = *this; - offset -= ++value; + vtable(operation::incr, handle, --value, nullptr); return orig; } [[nodiscard]] reference operator*() const { - return deref(handle, offset); + reference other; + vtable(operation::deref, handle, 0, &other); + return other; } [[nodiscard]] pointer operator->() const { @@ -1525,7 +1541,7 @@ public: } [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { - return offset == other.offset; + return handle == other.handle; } [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { @@ -1533,8 +1549,7 @@ public: } private: - deref_fn_type *deref; - difference_type offset; + vtable_type *vtable; any handle; }; @@ -1678,7 +1693,7 @@ inline bool meta_sequence_container::clear() { * @return A possibly invalid iterator to the inserted element. */ inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { - return insert_or_erase_fn(storage, it.offset, value); + return insert_or_erase_fn(storage, it.handle, value); } /** diff --git a/test/entt/meta/meta_container.cpp b/test/entt/meta/meta_container.cpp index 0f2293d25..0b6f05c16 100644 --- a/test/entt/meta/meta_container.cpp +++ b/test/entt/meta/meta_container.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -252,6 +253,53 @@ TEST_F(MetaContainer, StdList) { ASSERT_EQ(view.size(), 0u); } +TEST_F(MetaContainer, StdDeque) { + std::deque deque{}; + auto any = entt::forward_as_meta(deque); + auto view = any.as_sequence_container(); + + ASSERT_TRUE(view); + ASSERT_EQ(view.value_type(), entt::resolve()); + + ASSERT_EQ(view.size(), 0u); + ASSERT_EQ(view.begin(), view.end()); + ASSERT_TRUE(view.resize(3u)); + ASSERT_EQ(view.size(), 3u); + ASSERT_NE(view.begin(), view.end()); + + view[0].cast() = 2; + view[1].cast() = 3; + view[2].cast() = 4; + + ASSERT_EQ(view[1u].cast(), 3); + + auto it = view.begin(); + auto ret = view.insert(it, 0); + + ASSERT_TRUE(ret); + ASSERT_FALSE(view.insert(ret, invalid_type{})); + ASSERT_TRUE(view.insert(++ret, 1.)); + + ASSERT_EQ(view.size(), 5u); + ASSERT_EQ(view.begin()->cast(), 0); + ASSERT_EQ((++view.begin())->cast(), 1); + + ret = view.insert(view.end(), 42); + + ASSERT_TRUE(ret); + ASSERT_EQ(*ret, 42); + + it = view.begin(); + ret = view.erase(it); + + ASSERT_TRUE(ret); + ASSERT_EQ(view.size(), 5u); + ASSERT_EQ(ret->cast(), 1); + + ASSERT_TRUE(view.clear()); + ASSERT_EQ(view.size(), 0u); +} + TEST_F(MetaContainer, StdMap) { std::map map{{2, 'c'}, {3, 'd'}, {4, 'e'}}; auto any = entt::forward_as_meta(map);