meta: containers support (close #499)

This commit is contained in:
Michele Caini
2020-06-16 23:21:45 +02:00
parent 2e4becad60
commit 3400b32015
5 changed files with 668 additions and 58 deletions

View File

@@ -116,6 +116,8 @@ struct meta_type_node {
const bool is_function_pointer;
const bool is_member_object_pointer;
const bool is_member_function_pointer;
const bool is_sequence_container;
const bool is_associative_container;
const size_type rank;
size_type(* const extent)(size_type);
bool(* const compare)(const void *, const void *);
@@ -257,6 +259,8 @@ public:
std::is_pointer_v<Type> && std::is_function_v<std::remove_pointer_t<Type>>,
std::is_member_object_pointer_v<Type>,
std::is_member_function_pointer_v<Type>,
is_sequence_container_v<Type>,
is_associative_container_v<Type>,
std::rank_v<Type>,
[](meta_type_node::size_type dim) {
return extent(dim, std::make_index_sequence<std::rank_v<Type>>{});

View File

@@ -20,10 +20,60 @@ namespace entt {
class meta_type;
class meta_any;
/*! @brief Proxy object for containers of any type. */
class meta_container {
/*! @brief A meta_any is allowed to create proxies. */
friend class meta_any;
struct meta_iterator;
struct meta_view {
[[nodiscard]] virtual std::size_t size(void *) const ENTT_NOEXCEPT = 0;
[[nodiscard]] virtual bool insert(void *, meta_any, meta_any) = 0;
[[nodiscard]] virtual bool erase(void *, meta_any) = 0;
[[nodiscard]] virtual meta_any begin(void *) const ENTT_NOEXCEPT = 0;
[[nodiscard]] virtual meta_any end(void *) const ENTT_NOEXCEPT = 0;
[[nodiscard]] virtual meta_any find(void *, meta_any) const ENTT_NOEXCEPT = 0;
[[nodiscard]] virtual meta_any value(meta_any) const ENTT_NOEXCEPT = 0;
virtual void incr(meta_any) const ENTT_NOEXCEPT = 0;
};
meta_container(meta_view *impl, void *container)
: view{impl},
instance{container}
{}
public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Input iterator type. */
using iterator = meta_iterator;
/*! @brief Default constructor. */
meta_container()
: view{},
instance{}
{}
[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT;
inline bool insert(meta_any, meta_any);
inline bool erase(meta_any);
[[nodiscard]] inline iterator begin();
[[nodiscard]] inline iterator end();
[[nodiscard]] inline iterator operator[](meta_any pos_or_key);
[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT;
private:
meta_view *view;
void *instance;
};
/**
* @brief Opaque container for values of any type.
* @brief Opaque wrapper for values of any type.
*
* This class uses a technique called small buffer optimization (SBO) to get rid
* of memory allocations if possible. This should improve overall performance.
@@ -86,12 +136,158 @@ class meta_any {
}
};
meta_any(const internal::meta_type_node *curr, void *ref) ENTT_NOEXCEPT
: meta_any{}
{
node = curr;
instance = ref;
}
template<typename, typename = void>
struct container_view {
[[nodiscard]] static meta_container::meta_view * instance() {
return nullptr;
}
};
template<typename Type>
struct container_view<Type, std::enable_if_t<is_sequence_container_v<Type>>>: meta_container::meta_view {
[[nodiscard]] static meta_container::meta_view * instance() {
static container_view common{};
return &common;
}
[[nodiscard]] std::size_t size(void *container) const ENTT_NOEXCEPT override {
return static_cast<Type *>(container)->size();
}
[[nodiscard]] bool insert(void *container, meta_any it, meta_any value) override {
bool ret = false;
if constexpr(is_dynamic_sequence_container_v<Type>) {
if(auto *iter = it.try_cast<typename Type::iterator>(); iter) {
if(const auto *curr = value.try_cast<typename Type::value_type>(); curr) {
*iter = static_cast<Type *>(container)->insert(*iter, *curr);
ret = true;
}
}
}
return ret;
}
[[nodiscard]] bool erase(void *container, meta_any it) override {
bool ret = false;
if constexpr(is_dynamic_sequence_container_v<Type>) {
if(auto *iter = it.try_cast<typename Type::iterator>(); iter) {
*iter = static_cast<Type *>(container)->erase(*iter);
ret = true;
}
}
return ret;
}
[[nodiscard]] meta_any begin(void *container) const ENTT_NOEXCEPT override {
return static_cast<Type *>(container)->begin();
}
[[nodiscard]] meta_any end(void *container) const ENTT_NOEXCEPT override {
return static_cast<Type *>(container)->end();
}
[[nodiscard]] meta_any value(meta_any it) const ENTT_NOEXCEPT override {
return std::ref(*it.cast<typename Type::iterator>());
}
[[nodiscard]] meta_any find(void *container, meta_any idx) const ENTT_NOEXCEPT override {
meta_any any{};
if(const auto *curr = idx.try_cast<std::size_t>(); curr) {
any = std::next(static_cast<Type *>(container)->begin(), *curr);
}
return any;
}
void incr(meta_any it) const ENTT_NOEXCEPT override {
++it.cast<typename Type::iterator>();
}
};
template<typename Type>
struct container_view<Type, std::enable_if_t<is_associative_container_v<Type>>>: meta_container::meta_view {
[[nodiscard]] static meta_container::meta_view * instance() {
static container_view common{};
return &common;
}
[[nodiscard]] std::size_t size(void *container) const ENTT_NOEXCEPT override {
return static_cast<Type *>(container)->size();
}
[[nodiscard]] bool insert(void *container, meta_any key, meta_any value) override {
bool ret = false;
if constexpr(is_key_only_associative_container_v<Type>) {
if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
static_cast<Type *>(container)->insert(*curr);
ret = true;
}
} else {
if(const auto *k_curr = key.try_cast<typename Type::key_type>(); k_curr) {
if(const auto *v_curr = value.try_cast<typename Type::mapped_type>(); v_curr) {
static_cast<Type *>(container)->insert(std::make_pair(*k_curr, *v_curr));
ret = true;
}
}
}
return ret;
}
[[nodiscard]] bool erase(void *container, meta_any key) override {
bool ret = false;
if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
static_cast<Type *>(container)->erase(*curr);
ret = true;
}
return ret;
}
[[nodiscard]] meta_any begin(void *container) const ENTT_NOEXCEPT override {
return static_cast<Type *>(container)->begin();
}
[[nodiscard]] meta_any end(void *container) const ENTT_NOEXCEPT override {
return static_cast<Type *>(container)->end();
}
[[nodiscard]] meta_any value(meta_any it) const ENTT_NOEXCEPT override {
if constexpr(is_key_only_associative_container_v<Type>) {
return *it.cast<typename Type::iterator>();
} else {
return std::ref(*it.cast<typename Type::iterator>());
}
}
[[nodiscard]] meta_any find(void *container, meta_any key) const ENTT_NOEXCEPT override {
meta_any any{};
if constexpr(is_key_only_associative_container_v<Type>) {
if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
auto *cont = static_cast<Type *>(container);
any = std::find(cont->begin(), cont->end(), *curr);
}
} else {
if(const auto *curr = key.try_cast<typename Type::key_type>(); curr) {
any = static_cast<Type *>(container)->find(*curr);
}
}
return any;
}
void incr(meta_any it) const ENTT_NOEXCEPT override {
it.cast<typename Type::iterator>().operator++();
}
};
public:
/*! @brief Default constructor. */
@@ -101,12 +297,13 @@ public:
node{},
destroy_fn{},
copy_fn{},
steal_fn{}
steal_fn{},
cview{}
{}
/**
* @brief Constructs a meta any by directly initializing the new object.
* @tparam Type Type of object to use to initialize the container.
* @tparam Type Type of object to use to initialize the wrapper.
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
*/
@@ -121,23 +318,28 @@ public:
destroy_fn = &type_traits<Type>::destroy;
copy_fn = &type_traits<Type>::copy;
steal_fn = &type_traits<Type>::steal;
cview = container_view<Type>::instance();
}
}
/**
* @brief Constructs a meta any that holds an unmanaged object.
* @tparam Type Type of object to use to initialize the container.
* @param value An instance of an object to use to initialize the container.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type>
meta_any(std::reference_wrapper<Type> value)
: meta_any{internal::meta_info<Type>::resolve(), &value.get()}
{}
: meta_any{}
{
node = internal::meta_info<Type>::resolve();
instance = &value.get();
cview = container_view<Type>::instance();
}
/**
* @brief Constructs a meta any from a given value.
* @tparam Type Type of object to use to initialize the container.
* @param value An instance of an object to use to initialize the container.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
meta_any(Type &&value)
@@ -156,6 +358,7 @@ public:
destroy_fn = other.destroy_fn;
copy_fn = other.copy_fn;
steal_fn = other.steal_fn;
cview = other.cview;
}
/**
@@ -177,8 +380,8 @@ public:
/**
* @brief Assignment operator.
* @tparam Type Type of object to use to initialize the container.
* @param value An instance of an object to use to initialize the container.
* @tparam Type Type of object to use to initialize the wrapper.
* @param value An instance of an object to use to initialize the wrapper.
* @return This meta any object.
*/
template<typename Type>
@@ -309,9 +512,8 @@ public:
}
/**
* @brief Replaces the contained object by initializing a new instance
* directly.
* @tparam Type Type of object to use to initialize the container.
* @brief Replaces the contained object by creating a new instance directly.
* @tparam Type Type of object to use to initialize the wrapper.
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
*/
@@ -325,7 +527,19 @@ public:
* @return A meta any that shares a reference to an unmanaged object.
*/
[[nodiscard]] meta_any ref() const ENTT_NOEXCEPT {
return meta_any{node, instance};
meta_any alias{};
alias.node = node;
alias.instance = instance;
alias.cview = cview;
return alias;
}
/**
* @brief Returns a container view.
* @return A container view for the underlying object.
*/
[[nodiscard]] meta_container view() const ENTT_NOEXCEPT {
return { cview, instance };
}
/**
@@ -337,18 +551,17 @@ public:
}
/**
* @brief Returns false if a container is empty, true otherwise.
* @return False if the container is empty, true otherwise.
* @brief Returns false if a wrapper is empty, true otherwise.
* @return False if the wrapper is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(node == nullptr);
}
/**
* @brief Checks if two containers differ in their content.
* @param other Container with which to compare.
* @return False if the two containers differ in their content, true
* otherwise.
* @brief Checks if two wrappers differ in their content.
* @param other Wrapper with which to compare.
* @return False if the two objects differ in their content, true otherwise.
*/
[[nodiscard]] bool operator==(const meta_any &other) const {
return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(instance, other.instance));
@@ -377,6 +590,7 @@ public:
std::swap(lhs.destroy_fn, rhs.destroy_fn);
std::swap(lhs.copy_fn, rhs.copy_fn);
std::swap(lhs.steal_fn, rhs.steal_fn);
std::swap(lhs.cview, rhs.cview);
}
private:
@@ -386,20 +600,196 @@ private:
destroy_fn_type *destroy_fn;
copy_fn_type *copy_fn;
steal_fn_type *steal_fn;
meta_container::meta_view *cview;
};
/**
* @brief Checks if two containers differ in their content.
* @brief Checks if two wrappers differ in their content.
* @param lhs A meta any object, either empty or not.
* @param rhs A meta any object, either empty or not.
* @return True if the two containers differ in their content, false otherwise.
* @return True if the two wrappers differ in their content, false otherwise.
*/
[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT {
return !(lhs == rhs);
}
/*! @brief Opaque iterator for meta containers. */
struct meta_container::meta_iterator {
/*! @brief Signed integer type. */
using difference_type = std::ptrdiff_t;
/*! @brief Type of elements returned by the iterator. */
using value_type = meta_any;
/*! @brief Pointer type, `void` on purpose. */
using pointer = void;
/*! @brief Reference type, it is **not** an actual reference. */
using reference = value_type;
/*! @brief Iterator category. */
using iterator_category = std::input_iterator_tag;
/*! @brief Default constructor. */
meta_iterator() ENTT_NOEXCEPT = default;
/**
* @brief Constructs a meta iterator that wraps an actual iterator.
* @param ref A proxy object that _knows_ how to use the wrapped iterator.
* @param iter The actual iterator, properly wrapped.
*/
meta_iterator(meta_container::meta_view *ref, meta_any iter)
: view{ref},
it{std::move(iter)}
{}
/*! @brief Pre-increment operator. @return This iterator. */
meta_iterator & operator++() ENTT_NOEXCEPT {
return view->incr(handle()), *this;
}
/*! @brief Post-increment operator. @return This iterator. */
meta_iterator operator++(int) ENTT_NOEXCEPT {
iterator orig = *this;
return view->incr(handle()), orig;
}
/**
* @brief Checks if two meta iterators refer to the same element.
* @param other The meta iterator with which to compare.
* @return True if the two meta iterators refer to the same element, false
* otherwise.
*/
[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT {
return it == other.it;
}
/**
* @brief Checks if two meta iterators refer to the same element.
* @param other The meta iterator with which to compare.
* @return False if the two meta iterators refer to the same element, true
* otherwise.
*/
[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT {
return !(*this == other);
}
/**
* @brief Indirection operator.
* @return The element to which the meta pointer points.
*/
[[nodiscard]] reference operator*() const {
return view->value(handle());
}
/**
* @brief Returns a handle to the underlying iterator.
* @return The actual iterator, properly wrapped.
*/
[[nodiscard]] meta_any handle() const ENTT_NOEXCEPT {
return *it;
}
/**
* @brief Returns false if an iterator is invalid, true otherwise.
* @return False if the iterator is invalid, true otherwise.
*/
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return static_cast<bool>(it);
}
private:
meta_container::meta_view *view;
entt::meta_any it;
};
/**
* @brief Returns the number of elements.
* @return Number of elements.
*/
[[nodiscard]] meta_container::size_type meta_container::size() const ENTT_NOEXCEPT {
return view->size(instance);
}
/**
* @brief Inserts an element in the container.
*
* In case of sequence containers, the first parameter must be a valid iterator
* handle. In case of associative containers, the type of the key must be such
* that a cast or conversion to the key type of the container is possible.<br/>
* The type of the value must be such that a cast or conversion to the value
* type of the container is possible.
*
* @param it_or_key A valid iterator handle in case of sequence containers, a
* key in case of associative containers.
* @param value The element to insert in the container.
* @return True in case of success, false otherwise.
*/
inline bool meta_container::insert(meta_any it_or_key, meta_any value = {}) {
return view->insert(instance, std::move(it_or_key), std::move(value));
}
/**
* @brief Erases an element from the container.
*
* In case of sequence containers, the parameter must be a valid iterator
* handle. In case of associative containers, the type of the key must be such
* that a cast or conversion to the key type of the container is possible.
*
* @param it_or_key A valid iterator handle in case of sequence containers, a
* key in case of associative containers.
* @return True in case of success, false otherwise.
*/
inline bool meta_container::erase(meta_any it_or_key) {
return view->erase(instance, std::move(it_or_key));
}
/**
* @brief Returns an iterator to the first element of the container.
* @return An iterator to the first element of the container.
*/
[[nodiscard]] inline meta_container::iterator meta_container::begin() {
return {view, view->begin(instance)};
}
/**
* @brief Returns an iterator that is past the last element of the container.
* @return An iterator that is past the last element of the container.
*/
[[nodiscard]] inline meta_container::iterator meta_container::end() {
return {view, view->end(instance)};
}
/**
* @brief Returns an iterator to the required element.
*
* In case of sequence containers, the parameter must be a valid position (no
* bounds checking is performed) and such that a cast or conversion to size_type
* is possible. In case of associative containers, the type of the key must be
* such that a cast or conversion to the key type of the container is possible.
*
* @param pos_or_key A valid position in case of sequence containers, a key in
* case of associative containers.
* @return An iterator to the required element.
*/
[[nodiscard]] inline meta_container::iterator meta_container::operator[](meta_any pos_or_key) {
return {view, view->find(instance, std::move(pos_or_key))};
}
/**
* @brief Returns true if a meta container is valid, false otherwise.
* @return True if the meta container is valid, false otherwise.
*/
[[nodiscard]] meta_container::operator bool() const ENTT_NOEXCEPT {
return view;
}
/**
* @brief Opaque pointers to instances of any type.
*
@@ -415,8 +805,8 @@ struct meta_handle {
/**
* @brief Creates a handle that points to an unmanaged object.
* @tparam Type Type of object to use to initialize the container.
* @param value An instance of an object to use to initialize the container.
* @tparam Type Type of object to use to initialize the handle.
* @param value An instance of an object to use to initialize the handle.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_handle>>>
meta_handle(Type &&value) ENTT_NOEXCEPT
@@ -440,7 +830,7 @@ private:
};
/*! @brief Opaque container for meta properties of any type. */
/*! @brief Opaque wrapper for meta properties of any type. */
struct meta_prop {
/*! @brief Node type. */
using node_type = internal::meta_prop_node;
@@ -482,7 +872,7 @@ private:
};
/*! @brief Opaque container for meta base classes. */
/*! @brief Opaque wrapper for meta base classes. */
struct meta_base {
/*! @brief Node type. */
using node_type = internal::meta_base_node;
@@ -523,7 +913,7 @@ private:
};
/*! @brief Opaque container for meta conversion functions. */
/*! @brief Opaque wrapper for meta conversion functions. */
struct meta_conv {
/*! @brief Node type. */
using node_type = internal::meta_conv_node;
@@ -561,7 +951,7 @@ private:
};
/*! @brief Opaque container for meta constructors. */
/*! @brief Opaque wrapper for meta constructors. */
struct meta_ctor {
/*! @brief Node type. */
using node_type = internal::meta_ctor_node;
@@ -596,7 +986,7 @@ struct meta_ctor {
*
* To create a valid instance, the parameters must be such that a cast or
* conversion to the required types is possible. Otherwise, an empty and
* thus invalid container is returned.
* thus invalid wrapper is returned.
*
* @tparam Args Types of arguments to use to construct the instance.
* @param args Parameters to use to construct the instance.
@@ -653,7 +1043,7 @@ private:
};
/*! @brief Opaque container for meta data. */
/*! @brief Opaque wrapper for meta data. */
struct meta_data {
/*! @brief Node type. */
using node_type = internal::meta_data_node;
@@ -764,7 +1154,7 @@ private:
};
/*! @brief Opaque container for meta functions. */
/*! @brief Opaque wrapper for meta functions. */
struct meta_func {
/*! @brief Node type. */
using node_type = internal::meta_func_node;
@@ -826,7 +1216,7 @@ struct meta_func {
*
* To invoke a meta function, the parameters must be such that a cast or
* conversion to the required types is possible. Otherwise, an empty and
* thus invalid container is returned.<br/>
* thus invalid wrapper is returned.<br/>
* It must be possible to cast the instance to the parent type of the meta
* function. Otherwise, invoking the underlying function results in an
* undefined behavior.
@@ -890,7 +1280,7 @@ private:
};
/*! @brief Opaque container for meta types. */
/*! @brief Opaque wrapper for meta types. */
class meta_type {
template<typename... Args, std::size_t... Indexes>
[[nodiscard]] auto ctor(std::index_sequence<Indexes...>) const {
@@ -933,7 +1323,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to void or not.
* @brief Checks whether a type refers to void or not.
* @return True if the underlying type is void, false otherwise.
*/
[[nodiscard]] bool is_void() const ENTT_NOEXCEPT {
@@ -941,7 +1331,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to an integral type or not.
* @brief Checks whether a type refers to an integral type or not.
* @return True if the underlying type is an integral type, false otherwise.
*/
[[nodiscard]] bool is_integral() const ENTT_NOEXCEPT {
@@ -949,8 +1339,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to a floating-point type or
* not.
* @brief Checks whether a type refers to a floating-point type or not.
* @return True if the underlying type is a floating-point type, false
* otherwise.
*/
@@ -959,7 +1348,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to an array type or not.
* @brief Checks whether a type refers to an array type or not.
* @return True if the underlying type is an array type, false otherwise.
*/
[[nodiscard]] bool is_array() const ENTT_NOEXCEPT {
@@ -967,7 +1356,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to an enum or not.
* @brief Checks whether a type refers to an enum or not.
* @return True if the underlying type is an enum, false otherwise.
*/
[[nodiscard]] bool is_enum() const ENTT_NOEXCEPT {
@@ -975,7 +1364,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to an union or not.
* @brief Checks whether a type refers to an union or not.
* @return True if the underlying type is an union, false otherwise.
*/
[[nodiscard]] bool is_union() const ENTT_NOEXCEPT {
@@ -983,7 +1372,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to a class or not.
* @brief Checks whether a type refers to a class or not.
* @return True if the underlying type is a class, false otherwise.
*/
[[nodiscard]] bool is_class() const ENTT_NOEXCEPT {
@@ -991,7 +1380,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to a pointer or not.
* @brief Checks whether a type refers to a pointer or not.
* @return True if the underlying type is a pointer, false otherwise.
*/
[[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT {
@@ -999,7 +1388,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to a function pointer or not.
* @brief Checks whether a type refers to a function pointer or not.
* @return True if the underlying type is a function pointer, false
* otherwise.
*/
@@ -1008,8 +1397,7 @@ public:
}
/**
* @brief Indicates whether a meta type refers to a pointer to data member
* or not.
* @brief Checks whether a type refers to a pointer to data member or not.
* @return True if the underlying type is a pointer to data member, false
* otherwise.
*/
@@ -1018,8 +1406,8 @@ public:
}
/**
* @brief Indicates whether a meta type refers to a pointer to member
* function or not.
* @brief Checks whether a type refers to a pointer to member function or
* not.
* @return True if the underlying type is a pointer to member function,
* false otherwise.
*/
@@ -1028,7 +1416,25 @@ public:
}
/**
* @brief If a meta type refers to an array type, provides the number of
* @brief Checks whether a type refers to a sequence container or not.
* @return True if the underlying type is a sequence container, false
* otherwise.
*/
[[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT {
return node->is_sequence_container;
}
/**
* @brief Checks whether a type refers to an associative container or not.
* @return True if the underlying type is an associative container, false
* otherwise.
*/
[[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT {
return node->is_associative_container;
}
/**
* @brief If a type refers to an array type, provides the number of
* dimensions of the array.
* @return The number of dimensions of the array if the underlying type is
* an array type, 0 otherwise.
@@ -1038,8 +1444,8 @@ public:
}
/**
* @brief If a meta type refers to an array type, provides the number of
* elements along the given dimension of the array.
* @brief If a type refers to an array type, provides the number of elements
* along the given dimension of the array.
* @param dim The dimension of which to return the number of elements.
* @return The number of elements along the given dimension of the array if
* the underlying type is an array type, 0 otherwise.
@@ -1243,7 +1649,7 @@ public:
*
* To create a valid instance, the parameters must be such that a cast or
* conversion to the required types is possible. Otherwise, an empty and
* thus invalid container is returned.
* thus invalid wrapper is returned.
*
* @tparam Args Types of arguments to use to construct the instance.
* @param args Parameters to use to construct the instance.

View File

@@ -179,6 +179,7 @@ SETUP_BASIC_TEST(locator entt/locator/locator.cpp)
SETUP_BASIC_TEST(meta_any entt/meta/meta_any.cpp)
SETUP_BASIC_TEST(meta_base entt/meta/meta_base.cpp)
SETUP_BASIC_TEST(meta_container entt/meta/meta_container.cpp)
SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)

View File

@@ -0,0 +1,193 @@
#include <array>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <entt/core/hashed_string.hpp>
#include <entt/meta/factory.hpp>
#include <entt/meta/meta.hpp>
#include <entt/meta/resolve.hpp>
TEST(MetaContainer, Empty) {
entt::meta_container container{};
ASSERT_FALSE(container);
entt::meta_any any{std::vector<int>{}};
container = any.view();
ASSERT_TRUE(container);
}
TEST(MetaContainer, DynamicSequenceContainer) {
std::vector<int> vec{2, 3, 4};
entt::meta_any any{std::ref(vec)};
auto view = any.view();
ASSERT_TRUE(view);
ASSERT_EQ(view.size(), 3u);
auto first = view.begin();
const auto last = view.end();
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_NE(first, last);
ASSERT_EQ((*(first++)).cast<int>(), 2);
ASSERT_EQ((*(++first)).cast<int>(), 4);
ASSERT_NE(first++, last);
ASSERT_EQ(first, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
ASSERT_EQ((*view[std::size_t{1u}]).cast<int>(), 3);
auto it = view.begin();
ASSERT_TRUE(view.insert(it.handle(), 0));
ASSERT_TRUE(view.insert((++it).handle(), 1));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ((*view.begin()).cast<int>(), 0);
ASSERT_EQ((*++view.begin()).cast<int>(), 1);
it = view.begin();
ASSERT_TRUE(view.erase(it.handle()));
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ((*it).cast<int>(), 1);
(*view[std::size_t{}]).cast<int>() = 5;
ASSERT_EQ((*view.begin()).cast<int>(), 5);
}
TEST(MetaContainer, FixedSizeSequenceContainer) {
std::array<int, 3> arr{2, 3, 4};
entt::meta_any any{std::ref(arr)};
auto view = any.view();
ASSERT_TRUE(view);
ASSERT_EQ(view.size(), 3u);
auto first = view.begin();
const auto last = view.end();
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_NE(first, last);
ASSERT_EQ((*(first++)).cast<int>(), 2);
ASSERT_EQ((*(++first)).cast<int>(), 4);
ASSERT_NE(first++, last);
ASSERT_EQ(first, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
ASSERT_EQ((*view[std::size_t{1u}]).cast<int>(), 3);
auto it = view.begin();
ASSERT_FALSE(view.insert(it.handle(), 0));
ASSERT_FALSE(view.insert((++it).handle(), 1));
ASSERT_EQ(view.size(), 3u);
ASSERT_EQ((*view.begin()).cast<int>(), 2);
ASSERT_EQ((*++view.begin()).cast<int>(), 3);
it = view.begin();
ASSERT_FALSE(view.erase(it.handle()));
ASSERT_EQ(view.size(), 3u);
ASSERT_EQ((*it).cast<int>(), 2);
(*view[std::size_t{}]).cast<int>() = 5;
ASSERT_EQ((*view.begin()).cast<int>(), 5);
}
TEST(MetaContainer, KeyValueAssociativeContainer) {
std::map<int, char> map{{2, 'c'}, {3, 'd'}, {4, 'e'}};
entt::meta_any any{std::ref(map)};
auto view = any.view();
ASSERT_TRUE(view);
ASSERT_EQ(view.size(), 3u);
auto first = view.begin();
const auto last = view.end();
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_NE(first, last);
ASSERT_EQ(((*(first++)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{2, 'c'}));
ASSERT_EQ(((*(++first++)).cast<std::pair<const int, char>>()), (std::pair<const int, char>{4, 'e'}));
ASSERT_NE(first++, last);
ASSERT_EQ(first, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
ASSERT_EQ(((*view[3]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{3, 'd'}));
ASSERT_TRUE(view.insert(0, 'a'));
ASSERT_TRUE(view.insert(1, 'b'));
ASSERT_EQ(view.size(), 5u);
ASSERT_EQ(((*view[0]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{0, 'a'}));
ASSERT_EQ(((*view[1]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{1, 'b'}));
ASSERT_TRUE(view.erase(0));
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view[0], view.end());
(*view[1]).cast<std::pair<const int, char>>().second = 'f';
ASSERT_EQ(((*view[1]).cast<std::pair<const int, char>>()), (std::pair<const int, char>{1, 'f'}));
}
TEST(MetaContainer, KeyOnlyAssociativeContainer) {
std::set<int> set{2, 3, 4};
entt::meta_any any{std::ref(set)};
auto view = any.view();
ASSERT_TRUE(view);
ASSERT_EQ(view.size(), 3u);
auto first = view.begin();
const auto last = view.end();
ASSERT_FALSE(first == last);
ASSERT_TRUE(first != last);
ASSERT_NE(first, last);
ASSERT_EQ((*(first++)).cast<int>(), 2);
ASSERT_EQ((*(++first++)).cast<int>(), 4);
ASSERT_NE(first++, last);
ASSERT_EQ(first, last);
ASSERT_TRUE(first == last);
ASSERT_FALSE(first != last);
ASSERT_NE(view[3], view.end());
ASSERT_TRUE(view.insert(0, 'a'));
ASSERT_TRUE(view.insert(1));
ASSERT_EQ(view.size(), 5u);
ASSERT_NE(view[0], view.end());
ASSERT_NE(view[1], view.end());
ASSERT_TRUE(view.erase(0));
ASSERT_EQ(view.size(), 4u);
ASSERT_EQ(view[0], view.end());
}

View File

@@ -162,6 +162,12 @@ TEST_F(MetaType, Traits) {
ASSERT_TRUE(entt::resolve<decltype(&clazz_t::member)>().is_member_function_pointer());
ASSERT_FALSE(entt::resolve<decltype(&clazz_t::value)>().is_member_function_pointer());
ASSERT_TRUE(entt::resolve<std::vector<int>>().is_sequence_container());
ASSERT_FALSE((entt::resolve<std::map<int, char>>().is_sequence_container()));
ASSERT_TRUE((entt::resolve<std::map<int, char>>().is_associative_container()));
ASSERT_FALSE(entt::resolve<std::vector<int>>().is_associative_container());
ASSERT_EQ(entt::resolve<int>().rank(), 0u);
ASSERT_EQ(entt::resolve<int[5][3]>().rank(), 2u);
ASSERT_EQ(entt::resolve<int>().extent(), 0u);