* improved sequence container support for non-contiguous containers
* added support to (standard) deque as sequence containers
This commit is contained in:
Michele Caini
2022-07-14 08:45:59 +02:00
parent 131fd0d778
commit 61ef5a44e0
4 changed files with 103 additions and 28 deletions

1
TODO
View File

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

View File

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

View File

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

View File

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