any: comparison functions (==, !=)
This commit is contained in:
1
TODO
1
TODO
@@ -24,7 +24,6 @@ WIP:
|
||||
* HP: pass the registry to pools, basic poly storage should have only component member
|
||||
* HP: make view pack work also with groups, make packs input iterator only, add view adapter for external sources
|
||||
* HP: write documentation for custom storages and views!!
|
||||
* HP: add support for const references to any/poly (actual copy on copy, data vs cdata)
|
||||
* HP: any/poly: configurable sbo size, compile-time policies like sbo-required.
|
||||
* HP: registry: use a poly object for pools, no more pool_data type.
|
||||
* HP: make runtime views use opaque storage and therefore return also elements.
|
||||
|
||||
@@ -251,7 +251,17 @@ wrapper will be reconfigured by assigning it an object of a different type than
|
||||
the one contained, so as to be able to handle the new instance.<br/>
|
||||
When in doubt about the type of object contained, the `type` member function of
|
||||
`any` returns an instance of `type_info` associated with its element, or an
|
||||
invalid `type_info` object if the container is empty.
|
||||
invalid `type_info` object if the container is empty. The type is also used
|
||||
internally when comparing two `any` objects:
|
||||
|
||||
```cpp
|
||||
if(any == empty) { /* ... */ }
|
||||
```
|
||||
|
||||
In this case, before proceeding with a comparison, it's verified that the _type_
|
||||
of the two objects is actually the same.<br/>
|
||||
Refer to the `EnTT` type system documentation for more details on how
|
||||
`type_info` works and on possible risks of a comparison.
|
||||
|
||||
A particularly interesting feature of this class is that it can also be used as
|
||||
an opaque container for const and non-const references:
|
||||
|
||||
@@ -202,18 +202,9 @@ integrate it with the meta type system without having to duplicate the code.
|
||||
The API is very similar to that of the `any` type. The class `meta_any` _wraps_
|
||||
many of the feature to infer a meta node, before forwarding some or all of the
|
||||
arguments to the underlying storage.<br/>
|
||||
Among the few relevant differences, instances of `meta_any` are comparable,
|
||||
while those of `any` are not:
|
||||
|
||||
```cpp
|
||||
entt::meta_any any{42};
|
||||
entt::meta_any other{'c'};
|
||||
|
||||
const bool equal = (any == other);
|
||||
```
|
||||
|
||||
Also, `meta_any` adds support for containers and pointer-like types (see the
|
||||
following sections for more details).<br/>
|
||||
Among the few relevant differences, `meta_any` adds support for containers and
|
||||
pointer-like types (see the following sections for more details), while `any`
|
||||
does not.<br/>
|
||||
Similar to `any`, this class can also be used to create _aliases_ for unmanaged
|
||||
objects either upon construction using `std::ref` and `std::cref` or from an
|
||||
existing instance by means of the `as_ref` function. However, unlike `any`,
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace entt {
|
||||
|
||||
/*! @brief A SBO friendly, type-safe container for single values of any type. */
|
||||
class any {
|
||||
enum class operation { COPY, MOVE, DTOR, ADDR, CADDR, REF, CREF, TYPE };
|
||||
enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };
|
||||
|
||||
using storage_type = std::aligned_storage_t<sizeof(double[2]), alignof(double[2])>;
|
||||
using vtable_type = const void *(const operation, const any &, const void *);
|
||||
@@ -24,6 +24,15 @@ class any {
|
||||
template<typename Type>
|
||||
static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
|
||||
if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
|
||||
} else {
|
||||
return lhs == rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static type_info & as_type_info(const void *data) {
|
||||
return *const_cast<type_info *>(static_cast<const type_info *>(data));
|
||||
}
|
||||
@@ -50,6 +59,8 @@ class any {
|
||||
[[fallthrough]];
|
||||
case operation::DTOR:
|
||||
break;
|
||||
case operation::COMP:
|
||||
return compare<base_type>(from.instance, to) ? to : nullptr;
|
||||
case operation::ADDR:
|
||||
return std::is_const_v<base_type> ? nullptr : from.instance;
|
||||
case operation::CADDR:
|
||||
@@ -71,6 +82,8 @@ class any {
|
||||
case operation::DTOR:
|
||||
instance->~Type();
|
||||
break;
|
||||
case operation::COMP:
|
||||
return compare<Type>(instance, to) ? to : nullptr;
|
||||
case operation::ADDR:
|
||||
case operation::CADDR:
|
||||
return instance;
|
||||
@@ -98,6 +111,8 @@ class any {
|
||||
case operation::DTOR:
|
||||
delete static_cast<const Type *>(from.instance);
|
||||
break;
|
||||
case operation::COMP:
|
||||
return compare<Type>(from.instance, to) ? to : nullptr;
|
||||
case operation::ADDR:
|
||||
case operation::CADDR:
|
||||
return from.instance;
|
||||
@@ -188,7 +203,7 @@ public:
|
||||
/**
|
||||
* @brief Assignment operator.
|
||||
* @param other The instance to assign from.
|
||||
* @return This any any object.
|
||||
* @return This any object.
|
||||
*/
|
||||
any & operator=(any other) {
|
||||
swap(*this, other);
|
||||
@@ -237,6 +252,15 @@ public:
|
||||
return !(vtable(operation::CADDR, *this, nullptr) == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
bool operator==(const any &other) const ENTT_NOEXCEPT {
|
||||
return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two any objects.
|
||||
* @param lhs A valid any object.
|
||||
@@ -274,6 +298,17 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @param lhs A wrapper, either empty or not.
|
||||
* @param rhs A wrapper, either empty or not.
|
||||
* @return True if the two wrappers differ in their content, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline bool operator!=(const any &lhs, const any &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Performs type-safe access to the contained object.
|
||||
* @param data Target any object.
|
||||
|
||||
@@ -18,6 +18,10 @@ struct empty {
|
||||
~empty() { ++counter; }
|
||||
};
|
||||
|
||||
struct not_comparable {
|
||||
bool operator==(const not_comparable &) const = delete;
|
||||
};
|
||||
|
||||
TEST(Any, SBO) {
|
||||
entt::any any{'c'};
|
||||
|
||||
@@ -712,6 +716,57 @@ TEST(Any, AsRef) {
|
||||
ASSERT_NE(entt::any_cast<int>(&cref), any.data());
|
||||
}
|
||||
|
||||
TEST(Any, Comparable) {
|
||||
auto test = [](auto value, auto other) {
|
||||
entt::any any{value};
|
||||
|
||||
ASSERT_EQ(any, any);
|
||||
ASSERT_EQ(any, entt::any{value});
|
||||
ASSERT_NE(entt::any{other}, any);
|
||||
ASSERT_NE(any, entt::any{});
|
||||
|
||||
ASSERT_TRUE(any == any);
|
||||
ASSERT_TRUE(any == entt::any{value});
|
||||
ASSERT_FALSE(entt::any{other} == any);
|
||||
ASSERT_TRUE(any != entt::any{other});
|
||||
ASSERT_TRUE(entt::any{} != any);
|
||||
};
|
||||
|
||||
int value = 42;
|
||||
|
||||
test('c', 'a');
|
||||
test(fat{{.1, .2, .3, .4}}, fat{{.0, .1, .2, .3}});
|
||||
test(std::ref(value), 3);
|
||||
test(3, std::cref(value));
|
||||
}
|
||||
|
||||
TEST(Any, NotComparable) {
|
||||
entt::any any{not_comparable{}};
|
||||
|
||||
ASSERT_EQ(any, any);
|
||||
ASSERT_NE(any, entt::any{not_comparable{}});
|
||||
ASSERT_NE(entt::any{}, any);
|
||||
|
||||
ASSERT_TRUE(any == any);
|
||||
ASSERT_FALSE(any == entt::any{not_comparable{}});
|
||||
ASSERT_TRUE(entt::any{} != any);
|
||||
}
|
||||
|
||||
TEST(Any, CompareVoid) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
|
||||
ASSERT_EQ(any, any);
|
||||
ASSERT_EQ(any, entt::any{std::in_place_type<void>});
|
||||
ASSERT_NE(entt::any{'a'}, any);
|
||||
ASSERT_EQ(any, entt::any{});
|
||||
|
||||
ASSERT_TRUE(any == any);
|
||||
ASSERT_TRUE(any == entt::any{std::in_place_type<void>});
|
||||
ASSERT_FALSE(entt::any{'a'} == any);
|
||||
ASSERT_TRUE(any != entt::any{'a'});
|
||||
ASSERT_FALSE(entt::any{} != any);
|
||||
}
|
||||
|
||||
TEST(Any, AnyCast) {
|
||||
entt::any any{42};
|
||||
const auto &cany = any;
|
||||
|
||||
Reference in New Issue
Block a user