any: comparison functions (==, !=)

This commit is contained in:
Michele Caini
2020-12-21 12:25:22 +01:00
parent fa4c6bccbc
commit 2daaf899d3
5 changed files with 106 additions and 16 deletions

1
TODO
View File

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

View File

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

View File

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

View File

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

View File

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