Просмотр исходного кода

meta:
* improved sequence container support for non-contiguous containers
* added support to (standard) deque as sequence containers

Michele Caini 3 лет назад
Родитель
Сommit
61ef5a44e0
4 измененных файлов с 103 добавлено и 28 удалено
  1. 0 1
      TODO
  2. 20 7
      src/entt/meta/container.hpp
  3. 35 20
      src/entt/meta/meta.hpp
  4. 48 0
      test/entt/meta/meta_container.cpp

+ 0 - 1
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

+ 20 - 7
src/entt/meta/container.hpp

@@ -2,6 +2,7 @@
 #define ENTT_META_CONTAINER_HPP
 
 #include <array>
+#include <deque>
 #include <iterator>
 #include <list>
 #include <map>
@@ -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<Type>(&container); cont) {
-            return iterator{*cont, static_cast<typename iterator::difference_type>(as_end * cont->size())};
+            return iterator{as_end ? cont->end() : cont->begin()};
         }
 
         const Type &as_const = any_cast<const Type &>(container);
-        return iterator{as_const, static_cast<typename iterator::difference_type>(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<Type>::value) {
             if(auto *const cont = any_cast<Type>(&container); cont) {
                 if(value) {
                     // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
                     if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
                         const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
-                        const auto curr = cont->insert(std::next(cont->begin(), offset), element ? *element : value.cast<typename Type::value_type>());
-                        return iterator{*cont, static_cast<std::ptrdiff_t>(offset)};
+                        auto *it = any_cast<typename Type::iterator>(&handle);
+
+                        return iterator{cont->insert(
+                            it ? *it : any_cast<typename Type::const_iterator>(handle),
+                            element ? *element : value.cast<typename Type::value_type>())};
                     }
                 } else {
-                    const auto curr = cont->erase(std::next(cont->begin(), offset));
-                    return iterator{*cont, static_cast<std::ptrdiff_t>(offset)};
+                    auto *it = any_cast<typename Type::iterator>(&handle);
+                    return iterator{it ? cont->erase(*it) : cont->erase(any_cast<typename Type::const_iterator>(handle))};
                 }
             }
         }
@@ -178,6 +182,15 @@ template<typename Type, typename... Args>
 struct meta_sequence_container_traits<std::list<Type, Args...>>
     : internal::basic_meta_sequence_container_traits<std::list<Type, Args...>> {};
 
+/**
+ * @brief Meta sequence container traits for `std::deque`s of any type.
+ * @tparam Type The type of elements.
+ * @tparam Args Other arguments.
+ */
+template<typename Type, typename... Args>
+struct meta_sequence_container_traits<std::deque<Type, Args...>>
+    : internal::basic_meta_sequence_container_traits<std::deque<Type, Args...>> {};
+
 /**
  * @brief Meta associative container traits for `std::map`s of any type.
  * @tparam Key The key type of elements.

+ 35 - 20
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<typename It>
-    static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) {
-        return meta_any{std::in_place_type<typename std::iterator_traits<It>::reference>, *std::next(any_cast<const It &>(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<It &>(const_cast<any &>(value));
+            it = std::next(it, offset);
+        } break;
+        case operation::deref: {
+            const auto &it = any_cast<const It &>(value);
+            other->emplace<decltype(*it)>(*it);
+        } break;
+        }
     }
 
 public:
@@ -1482,38 +1496,40 @@ public:
     using iterator_category = std::input_iterator_tag;
 
     constexpr meta_iterator() noexcept
-        : deref{},
-          offset{},
+        : vtable{},
           handle{} {}
 
-    template<typename Type>
-    explicit meta_iterator(Type &cont, const difference_type init) noexcept
-        : deref{&deref_fn<decltype(cont.begin())>},
-          offset{init},
-          handle{cont.begin()} {}
+    template<typename It>
+    explicit meta_iterator(It iter) noexcept
+        : vtable{&basic_vtable<It>},
+          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);
 }
 
 /**

+ 48 - 0
test/entt/meta/meta_container.cpp

@@ -1,4 +1,5 @@
 #include <array>
+#include <deque>
 #include <list>
 #include <map>
 #include <set>
@@ -252,6 +253,53 @@ TEST_F(MetaContainer, StdList) {
     ASSERT_EQ(view.size(), 0u);
 }
 
+TEST_F(MetaContainer, StdDeque) {
+    std::deque<int> deque{};
+    auto any = entt::forward_as_meta(deque);
+    auto view = any.as_sequence_container();
+
+    ASSERT_TRUE(view);
+    ASSERT_EQ(view.value_type(), entt::resolve<int>());
+
+    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<int &>() = 2;
+    view[1].cast<int &>() = 3;
+    view[2].cast<int &>() = 4;
+
+    ASSERT_EQ(view[1u].cast<int>(), 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<int>(), 0);
+    ASSERT_EQ((++view.begin())->cast<int>(), 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<int>(), 1);
+
+    ASSERT_TRUE(view.clear());
+    ASSERT_EQ(view.size(), 0u);
+}
+
 TEST_F(MetaContainer, StdMap) {
     std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
     auto any = entt::forward_as_meta(map);