Skip to content

Commit caae7bf

Browse files
committed
SFINAE friendly comparison operators
1 parent d4014c7 commit caae7bf

File tree

3 files changed

+116
-14
lines changed

3 files changed

+116
-14
lines changed

src/entt/entity/entity.hpp

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
2323
};
2424

2525
template<typename Type>
26-
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
26+
struct entt_traits<Type, std::void_t<typename Type::entity_type>>
2727
: entt_traits<typename Type::entity_type> {
2828
using value_type = Type;
2929
};
@@ -57,8 +57,12 @@ struct entt_traits<std::uint64_t> {
5757
* @brief Common basic entity traits implementation.
5858
* @tparam Traits Actual entity traits to use.
5959
*/
60+
61+
template<typename Traits, typename = void>
62+
class basic_entt_traits;
63+
6064
template<typename Traits>
61-
class basic_entt_traits {
65+
class basic_entt_traits<Traits, std::void_t<decltype(sizeof(Traits))>> {
6266
static constexpr auto length = popcount(Traits::entity_mask);
6367

6468
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
@@ -159,8 +163,11 @@ class basic_entt_traits {
159163
* @brief Entity traits.
160164
* @tparam Type Type of identifier.
161165
*/
166+
template<typename Type, typename = void>
167+
struct entt_traits;
168+
162169
template<typename Type>
163-
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
170+
struct entt_traits<Type, std::void_t<decltype(sizeof(basic_entt_traits<internal::entt_traits<Type>>))>>: basic_entt_traits<internal::entt_traits<Type>> {
164171
/*! @brief Base type. */
165172
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
166173
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
@@ -207,7 +214,7 @@ struct null_t {
207214
* @tparam Entity Type of identifier.
208215
* @return The null representation for the given type.
209216
*/
210-
template<typename Entity>
217+
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
211218
[[nodiscard]] constexpr operator Entity() const noexcept {
212219
using traits_type = entt_traits<Entity>;
213220
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
@@ -238,7 +245,7 @@ struct null_t {
238245
* @param entity Identifier with which to compare.
239246
* @return False if the two elements differ, true otherwise.
240247
*/
241-
template<typename Entity>
248+
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
242249
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
243250
using traits_type = entt_traits<Entity>;
244251
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
@@ -251,7 +258,8 @@ struct null_t {
251258
* @return True if the two elements differ, false otherwise.
252259
*/
253260
template<typename Entity>
254-
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
261+
[[nodiscard]] constexpr auto operator!=(const Entity entity) const noexcept
262+
-> decltype(this->operator==(entity)) {
255263
return !(entity == *this);
256264
}
257265
};
@@ -264,7 +272,8 @@ struct null_t {
264272
* @return False if the two elements differ, true otherwise.
265273
*/
266274
template<typename Entity>
267-
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
275+
[[nodiscard]] constexpr auto operator==(const Entity lhs, const null_t rhs) noexcept
276+
-> decltype(rhs.operator==(lhs)) {
268277
return rhs.operator==(lhs);
269278
}
270279

@@ -276,7 +285,8 @@ template<typename Entity>
276285
* @return True if the two elements differ, false otherwise.
277286
*/
278287
template<typename Entity>
279-
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
288+
[[nodiscard]] constexpr auto operator!=(const Entity lhs, const null_t rhs) noexcept
289+
-> decltype(rhs.operator==(lhs)) {
280290
return !(rhs == lhs);
281291
}
282292

@@ -287,7 +297,7 @@ struct tombstone_t {
287297
* @tparam Entity Type of identifier.
288298
* @return The tombstone representation for the given type.
289299
*/
290-
template<typename Entity>
300+
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
291301
[[nodiscard]] constexpr operator Entity() const noexcept {
292302
using traits_type = entt_traits<Entity>;
293303
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
@@ -318,7 +328,7 @@ struct tombstone_t {
318328
* @param entity Identifier with which to compare.
319329
* @return False if the two elements differ, true otherwise.
320330
*/
321-
template<typename Entity>
331+
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
322332
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
323333
using traits_type = entt_traits<Entity>;
324334

@@ -336,8 +346,9 @@ struct tombstone_t {
336346
* @return True if the two elements differ, false otherwise.
337347
*/
338348
template<typename Entity>
339-
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
340-
return !(entity == *this);
349+
[[nodiscard]] constexpr auto operator!=(const Entity entity) const noexcept
350+
-> decltype(this->operator==(entity)) {
351+
return !(*this == entity);
341352
}
342353
};
343354

@@ -349,7 +360,8 @@ struct tombstone_t {
349360
* @return False if the two elements differ, true otherwise.
350361
*/
351362
template<typename Entity>
352-
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
363+
[[nodiscard]] constexpr auto operator==(const Entity lhs, const tombstone_t rhs) noexcept
364+
-> decltype(rhs.operator==(lhs)) {
353365
return rhs.operator==(lhs);
354366
}
355367

@@ -361,7 +373,8 @@ template<typename Entity>
361373
* @return True if the two elements differ, false otherwise.
362374
*/
363375
template<typename Entity>
364-
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
376+
[[nodiscard]] constexpr auto operator!=(const Entity lhs, const tombstone_t rhs) noexcept
377+
-> decltype(rhs.operator==(lhs)) {
365378
return !(rhs == lhs);
366379
}
367380

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ SETUP_BASIC_TEST(utility entt/core/utility.cpp)
234234

235235
SETUP_BASIC_TEST(component entt/entity/component.cpp)
236236
SETUP_BASIC_TEST(entity entt/entity/entity.cpp)
237+
SETUP_BASIC_TEST(sfinae entt/entity/sfinae.cpp)
237238
SETUP_BASIC_TEST(group entt/entity/group.cpp)
238239
SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
239240
SETUP_BASIC_TEST(helper entt/entity/helper.cpp)

test/entt/entity/sfinae.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#include <cstddef>
2+
#include <cstdint>
3+
#include <type_traits>
4+
#include <utility>
5+
#include <gtest/gtest.h>
6+
#include <entt/config/config.h>
7+
#include <entt/entity/entity.hpp>
8+
#include "../../common/entity.h"
9+
10+
template<typename T, typename U = T, typename = void>
11+
inline constexpr bool is_equality_comparable_v = false;
12+
13+
template<typename T, typename U>
14+
inline constexpr bool is_equality_comparable_v<T, U, std::void_t<decltype(std::declval<T>() == std::declval<U>())>> = true;
15+
16+
template<typename T, typename U = T, typename = void>
17+
inline constexpr bool is_not_equality_comparable_v = false;
18+
19+
template<typename T, typename U>
20+
inline constexpr bool is_not_equality_comparable_v<T, U, std::void_t<decltype(std::declval<T>() != std::declval<U>())>> = true;
21+
22+
template<typename T, typename U = T>
23+
inline constexpr bool is_comparable_v = is_equality_comparable_v<T, U>
24+
&& is_equality_comparable_v<U, T>
25+
&& is_not_equality_comparable_v<T, U>
26+
&& is_not_equality_comparable_v<U, T>;
27+
struct unrelated {};
28+
struct use_my_operator {};
29+
template<typename T>
30+
bool operator==(use_my_operator, T &&);
31+
32+
template<typename T>
33+
bool operator!=(use_my_operator, T &&);
34+
35+
template<typename T>
36+
bool operator==(T &&, use_my_operator);
37+
38+
template<typename T>
39+
bool operator!=(T &&, use_my_operator);
40+
41+
struct entity_traits {
42+
using value_type = test::entity;
43+
using entity_type = std::uint32_t;
44+
using version_type = std::uint16_t;
45+
static constexpr entity_type entity_mask = 0x3FFFF; // 18b
46+
static constexpr entity_type version_mask = 0x0FFF; // 12b
47+
};
48+
49+
struct other_entity_traits {
50+
using value_type = test::other_entity;
51+
using entity_type = std::uint32_t;
52+
using version_type = std::uint16_t;
53+
static constexpr entity_type entity_mask = 0xFFFFFFFF; // 32b
54+
static constexpr entity_type version_mask = 0x00; // 0b
55+
};
56+
57+
template<>
58+
struct entt::entt_traits<test::entity>: entt::basic_entt_traits<entity_traits> {
59+
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
60+
};
61+
62+
template<>
63+
struct entt::entt_traits<test::other_entity>: entt::basic_entt_traits<other_entity_traits> {
64+
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
65+
};
66+
67+
TEST(Sfinae, NullComparison) {
68+
static_assert(is_comparable_v<entt::null_t>);
69+
static_assert(is_comparable_v<entt::null_t, entt::entity>);
70+
static_assert(is_comparable_v<entt::null_t, test::entity>);
71+
static_assert(is_comparable_v<entt::null_t, test::other_entity>);
72+
73+
static_assert(is_comparable_v<use_my_operator, entt::null_t>);
74+
75+
static_assert(!is_comparable_v<entt::null_t, unrelated>);
76+
}
77+
78+
TEST(Sfinae, TombstoneComparison) {
79+
static_assert(is_comparable_v<entt::tombstone_t>);
80+
static_assert(is_comparable_v<entt::tombstone_t, test::entity>);
81+
static_assert(is_comparable_v<entt::tombstone_t, test::other_entity>);
82+
83+
static_assert(is_comparable_v<use_my_operator, entt::tombstone_t>);
84+
85+
static_assert(!is_comparable_v<entt::tombstone_t, unrelated>);
86+
87+
static_assert(!is_comparable_v<entt::tombstone_t, entt::null_t>);
88+
}

0 commit comments

Comments
 (0)