meta: support for std::vector<bool> and the like

This commit is contained in:
Michele Caini
2021-01-29 17:49:09 +01:00
parent 309fb0fa83
commit a03a569534
4 changed files with 106 additions and 57 deletions

View File

@@ -24,7 +24,10 @@ namespace entt {
* @tparam Trait Traits associated with the underlying container.
*/
template<typename Container, template<typename> class... Trait>
struct meta_container_traits: public Trait<Container>... {};
struct meta_container_traits: public Trait<Container>... {
/*! @brief Type of container. */
using type = Container;
};
/**
@@ -33,21 +36,12 @@ struct meta_container_traits: public Trait<Container>... {};
*/
template<typename Container>
struct basic_container {
/*! @brief Iterator type of the container. */
using iterator = typename Container::iterator;
/*! @brief Iterator type of the container. */
using const_iterator = typename Container::const_iterator;
/*! @brief Unsigned integer type. */
using size_type = typename Container::size_type;
/*! @brief Value type of the container. */
using value_type = typename Container::value_type;
/**
* @brief Returns the size of the given container.
* @param cont The container for which to return the size.
* @return The size of the given container.
*/
[[nodiscard]] static size_type size(const Container &cont) ENTT_NOEXCEPT {
[[nodiscard]] static typename Container::size_type size(const Container &cont) ENTT_NOEXCEPT {
return cont.size();
}
@@ -56,7 +50,7 @@ struct basic_container {
* @param cont The container for which to return the iterator.
* @return An iterator to the first element of the given container.
*/
[[nodiscard]] static iterator begin(Container &cont) {
[[nodiscard]] static typename Container::iterator begin(Container &cont) {
return cont.begin();
}
@@ -65,7 +59,7 @@ struct basic_container {
* @param cont The container for which to return the iterator.
* @return An iterator to the first element of the given container.
*/
[[nodiscard]] static const_iterator cbegin(const Container &cont) {
[[nodiscard]] static typename Container::const_iterator cbegin(const Container &cont) {
return cont.begin();
}
@@ -74,7 +68,7 @@ struct basic_container {
* @param cont The container for which to return the iterator.
* @return An iterator past the last element of the given container.
*/
[[nodiscard]] static iterator end(Container &cont) {
[[nodiscard]] static typename Container::iterator end(Container &cont) {
return cont.end();
}
@@ -83,7 +77,7 @@ struct basic_container {
* @param cont The container for which to return the iterator.
* @return An iterator past the last element of the given container.
*/
[[nodiscard]] static const_iterator cend(const Container &cont) {
[[nodiscard]] static typename Container::const_iterator cend(const Container &cont) {
return cont.end();
}
};
@@ -95,9 +89,6 @@ struct basic_container {
*/
template<typename Container>
struct basic_associative_container {
/*! @brief Key type of the sequence container. */
using key_type = typename Container::key_type;
/**
* @brief Returns an iterator to the element with key equivalent to the
* given one, if any.
@@ -105,12 +96,12 @@ struct basic_associative_container {
* @param key The key of the element to search.
* @return An iterator to the element with the given key, if any.
*/
[[nodiscard]] static typename Container::iterator find(Container &cont, const key_type &key) {
[[nodiscard]] static typename Container::iterator find(Container &cont, const typename Container::key_type &key) {
return cont.find(key);
}
/*! @copydoc find */
[[nodiscard]] static typename Container::const_iterator cfind(const Container &cont, const key_type &key) {
[[nodiscard]] static typename Container::const_iterator cfind(const Container &cont, const typename Container::key_type &key) {
return cont.find(key);
}
};
@@ -165,12 +156,12 @@ struct basic_sequence_container {
* @param pos The position of the element to return.
* @return A reference to the requested element.
*/
[[nodiscard]] static typename Container::value_type & get(Container &cont, typename Container::size_type pos) {
[[nodiscard]] static typename Container::reference get(Container &cont, typename Container::size_type pos) {
return cont[pos];
}
/*! @copydoc get */
[[nodiscard]] static const typename Container::value_type & cget(const Container &cont, typename Container::size_type pos) {
[[nodiscard]] static typename Container::const_reference cget(const Container &cont, typename Container::size_type pos) {
return cont[pos];
}
};
@@ -349,10 +340,7 @@ struct meta_associative_container_traits<std::map<Key, Value, Args...>>
basic_dynamic_associative_container,
dynamic_associative_key_value_container
>
{
/*! @brief Mapped type of the sequence container. */
using mapped_type = typename std::map<Key, Value, Args...>::mapped_type;
};
{};
/**
@@ -372,10 +360,7 @@ struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>
basic_dynamic_associative_container,
dynamic_associative_key_value_container
>
{
/*! @brief Mapped type of the sequence container. */
using mapped_type = typename std::unordered_map<Key, Value, Args...>::mapped_type;
};
{};
/**

View File

@@ -1787,7 +1787,11 @@ class meta_sequence_container::meta_iterator {
++any_cast<It &>(const_cast<any &>(from));
break;
case operation::DEREF:
*static_cast<meta_any *>(to) = std::reference_wrapper{*any_cast<const It &>(from)};
if constexpr(std::is_lvalue_reference_v<typename std::iterator_traits<It>::reference>) {
*static_cast<meta_any *>(to) = std::reference_wrapper{*any_cast<const It &>(from)};
} else {
*static_cast<meta_any *>(to) = *any_cast<const It &>(from);
}
break;
}
}
@@ -1866,8 +1870,8 @@ public:
}
private:
vtable_type *vtable;
any handle;
vtable_type *vtable{};
any handle{};
};
@@ -1876,7 +1880,7 @@ struct meta_sequence_container::meta_sequence_container_proxy {
using traits_type = meta_sequence_container_traits<Type>;
[[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT {
return internal::meta_info<typename traits_type::value_type>::resolve();
return internal::meta_info<typename Type::value_type>::resolve();
}
[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
@@ -1910,9 +1914,13 @@ struct meta_sequence_container::meta_sequence_container_proxy {
}
[[nodiscard]] static std::pair<iterator, bool> insert(any &container, iterator it, meta_any &value) {
if(auto * const cont = any_cast<Type>(&container); cont && value.allow_cast<const typename traits_type::value_type &>()) {
auto ret = traits_type::insert(*cont, any_cast<const typename traits_type::iterator &>(it.handle), value.cast<const typename traits_type::value_type &>());
return { iterator{std::move(ret.first)}, ret.second };
if(auto * const cont = any_cast<Type>(&container); cont) {
// 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>>();
auto ret = traits_type::insert(*cont, any_cast<const typename Type::iterator &>(it.handle), element ? *element : value.cast<typename Type::value_type>());
return { iterator{std::move(ret.first)}, ret.second };
}
}
return {};
@@ -1920,7 +1928,7 @@ struct meta_sequence_container::meta_sequence_container_proxy {
[[nodiscard]] static std::pair<iterator, bool> erase(any &container, iterator it) {
if(auto * const cont = any_cast<Type>(&container); cont) {
auto ret = traits_type::erase(*cont, any_cast<const typename traits_type::iterator &>(it.handle));
auto ret = traits_type::erase(*cont, any_cast<const typename Type::iterator &>(it.handle));
return { iterator{std::move(ret.first)}, ret.second };
}
@@ -1929,10 +1937,18 @@ struct meta_sequence_container::meta_sequence_container_proxy {
[[nodiscard]] static meta_any get(any &container, size_type pos) {
if(auto * const cont = any_cast<Type>(&container); cont) {
return std::reference_wrapper{traits_type::get(*cont, pos)};
if constexpr(std::is_lvalue_reference_v<typename Type::reference>) {
return std::reference_wrapper{traits_type::get(*cont, pos)};
} else {
return traits_type::get(*cont, pos);
}
}
return std::reference_wrapper{traits_type::cget(any_cast<const Type &>(container), pos)};
if constexpr(std::is_lvalue_reference_v<typename Type::const_reference>) {
return std::reference_wrapper{traits_type::cget(any_cast<const Type &>(container), pos)};
} else {
return traits_type::cget(any_cast<const Type &>(container), pos);
}
}
};
@@ -2132,8 +2148,8 @@ public:
}
private:
vtable_type *vtable;
any handle;
vtable_type *vtable{};
any handle{};
};
@@ -2142,19 +2158,19 @@ struct meta_associative_container::meta_associative_container_proxy {
using traits_type = meta_associative_container_traits<Type>;
[[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT {
return internal::meta_info<typename traits_type::key_type>::resolve();
return internal::meta_info<typename Type::key_type>::resolve();
}
[[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT {
if constexpr(is_key_only_meta_associative_container_v<Type>) {
return meta_type{};
} else {
return internal::meta_info<typename traits_type::mapped_type>::resolve();
return internal::meta_info<typename Type::mapped_type>::resolve();
}
}
[[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT {
return internal::meta_info<typename traits_type::value_type>::resolve();
return internal::meta_info<typename Type::value_type>::resolve();
}
[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT {
@@ -2183,12 +2199,12 @@ struct meta_associative_container::meta_associative_container_proxy {
}
[[nodiscard]] static bool insert(any &container, meta_any &key, meta_any &value) {
if(auto * const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename traits_type::key_type &>()) {
if(auto * const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
if constexpr(is_key_only_meta_associative_container_v<Type>) {
return traits_type::insert(*cont, key.cast<const typename traits_type::key_type &>());
return traits_type::insert(*cont, key.cast<const typename Type::key_type &>());
} else {
if(value.allow_cast<const typename traits_type::mapped_type &>()) {
return traits_type::insert(*cont, key.cast<const typename traits_type::key_type &>(), value.cast<const typename traits_type::mapped_type &>());
if(value.allow_cast<const typename Type::mapped_type &>()) {
return traits_type::insert(*cont, key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>());
}
}
}
@@ -2197,20 +2213,20 @@ struct meta_associative_container::meta_associative_container_proxy {
}
[[nodiscard]] static bool erase(any &container, meta_any &key) {
if(auto * const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename traits_type::key_type &>()) {
return traits_type::erase(*cont, key.cast<const typename traits_type::key_type &>());
if(auto * const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
return traits_type::erase(*cont, key.cast<const typename Type::key_type &>());
}
return false;
}
[[nodiscard]] static iterator find(any &container, meta_any &key) {
if(key.allow_cast<const typename traits_type::key_type &>()) {
if(key.allow_cast<const typename Type::key_type &>()) {
if(auto * const cont = any_cast<Type>(&container); cont) {
return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::find(*cont, key.cast<const typename traits_type::key_type &>())};
return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::find(*cont, key.cast<const typename Type::key_type &>())};
}
return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::cfind(any_cast<const Type &>(container), key.cast<const typename traits_type::key_type &>())};
return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::cfind(any_cast<const Type &>(container), key.cast<const typename Type::key_type &>())};
}
return {};

View File

@@ -35,7 +35,7 @@ struct has_meta_sequence_container_traits: std::false_type {};
/*! @copydoc has_meta_sequence_container_traits */
template<typename Type>
struct has_meta_sequence_container_traits<Type, std::void_t<typename meta_sequence_container_traits<Type>::value_type>>
struct has_meta_sequence_container_traits<Type, std::void_t<typename meta_sequence_container_traits<Type>::type::value_type>>
: std::true_type
{};
@@ -59,7 +59,7 @@ struct has_meta_associative_container_traits: std::false_type {};
/*! @copydoc has_meta_associative_container_traits */
template<typename Type>
struct has_meta_associative_container_traits<Type, std::void_t<typename meta_associative_container_traits<Type>::key_type>>
struct has_meta_associative_container_traits<Type, std::void_t<typename meta_associative_container_traits<Type>::type::key_type>>
: std::true_type
{};
@@ -83,7 +83,7 @@ struct is_key_only_meta_associative_container: std::true_type {};
/*! @copydoc is_key_only_meta_associative_container */
template<typename Type>
struct is_key_only_meta_associative_container<Type, std::void_t<typename meta_associative_container_traits<Type>::mapped_type>>
struct is_key_only_meta_associative_container<Type, std::void_t<typename meta_associative_container_traits<Type>::type::mapped_type>>
: std::false_type
{};

View File

@@ -436,3 +436,51 @@ TEST_F(MetaContainer, KeyOnlyAssociativeContainerConstMetaAny) {
test(std::ref(set));
test(std::cref(set));
}
TEST_F(MetaContainer, StdVectorBool) {
using proxy_type = typename std::vector<bool>::reference;
using const_proxy_type = typename std::vector<bool>::const_reference;
std::vector<bool> vec{};
entt::meta_any any{std::ref(vec)};
auto cany = as_ref(std::as_const(any));
auto view = any.as_sequence_container();
auto cview = cany.as_sequence_container();
ASSERT_TRUE(view);
ASSERT_EQ(view.value_type(), entt::resolve<bool>());
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<proxy_type>() = true;
view[1].cast<proxy_type>() = true;
view[2].cast<proxy_type>() = false;
ASSERT_EQ(cview[1u].cast<const_proxy_type>(), true);
auto it = view.begin();
auto ret = view.insert(it, true);
ASSERT_TRUE(ret.second);
ASSERT_FALSE(view.insert(ret.first, 'c').second);
ASSERT_TRUE(view.insert(++ret.first, false).second);
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ((*view.begin()).cast<proxy_type>(), true);
ASSERT_EQ((*++cview.begin()).cast<const_proxy_type>(), false);
it = view.begin();
ret = view.erase(it);
ASSERT_TRUE(ret.second);
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ((*ret.first).cast<proxy_type>(), false);
ASSERT_TRUE(view.clear());
ASSERT_EQ(cview.size(), 0u);
}