diff --git a/src/entt/entity/entity.hpp b/src/entt/entity/entity.hpp index 3c3dae697..91b5aa6c8 100644 --- a/src/entt/entity/entity.hpp +++ b/src/entt/entity/entity.hpp @@ -23,7 +23,7 @@ struct entt_traits>> }; template -struct entt_traits>> +struct entt_traits> : entt_traits { using value_type = Type; }; @@ -57,8 +57,12 @@ struct entt_traits { * @brief Common basic entity traits implementation. * @tparam Traits Actual entity traits to use. */ + +template +class basic_entt_traits; + template -class basic_entt_traits { +class basic_entt_traits> { static constexpr auto length = popcount(Traits::entity_mask); static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask"); @@ -159,8 +163,11 @@ class basic_entt_traits { * @brief Entity traits. * @tparam Type Type of identifier. */ +template +struct entt_traits; + template -struct entt_traits: basic_entt_traits> { +struct entt_traits>))>>: basic_entt_traits> { /*! @brief Base type. */ using base_type = basic_entt_traits>; /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ @@ -207,7 +214,7 @@ struct null_t { * @tparam Entity Type of identifier. * @return The null representation for the given type. */ - template + template)) * = nullptr> [[nodiscard]] constexpr operator Entity() const noexcept { using traits_type = entt_traits; constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); @@ -238,7 +245,7 @@ struct null_t { * @param entity Identifier with which to compare. * @return False if the two elements differ, true otherwise. */ - template + template)) * = nullptr> [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { using traits_type = entt_traits; return traits_type::to_entity(entity) == traits_type::to_entity(*this); @@ -251,7 +258,8 @@ struct null_t { * @return True if the two elements differ, false otherwise. */ template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + [[nodiscard]] constexpr auto operator!=(const Entity entity) const noexcept + -> decltype(this->operator==(entity)) { return !(entity == *this); } }; @@ -264,7 +272,8 @@ struct null_t { * @return False if the two elements differ, true otherwise. */ template -[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept { +[[nodiscard]] constexpr auto operator==(const Entity lhs, const null_t rhs) noexcept + -> decltype(rhs.operator==(lhs)) { return rhs.operator==(lhs); } @@ -276,7 +285,8 @@ template * @return True if the two elements differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept { +[[nodiscard]] constexpr auto operator!=(const Entity lhs, const null_t rhs) noexcept + -> decltype(rhs.operator==(lhs)) { return !(rhs == lhs); } @@ -287,7 +297,7 @@ struct tombstone_t { * @tparam Entity Type of identifier. * @return The tombstone representation for the given type. */ - template + template)) * = nullptr> [[nodiscard]] constexpr operator Entity() const noexcept { using traits_type = entt_traits; constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); @@ -318,7 +328,7 @@ struct tombstone_t { * @param entity Identifier with which to compare. * @return False if the two elements differ, true otherwise. */ - template + template)) * = nullptr> [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { using traits_type = entt_traits; @@ -336,8 +346,9 @@ struct tombstone_t { * @return True if the two elements differ, false otherwise. */ template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { - return !(entity == *this); + [[nodiscard]] constexpr auto operator!=(const Entity entity) const noexcept + -> decltype(this->operator==(entity)) { + return !(*this == entity); } }; @@ -349,7 +360,8 @@ struct tombstone_t { * @return False if the two elements differ, true otherwise. */ template -[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept { +[[nodiscard]] constexpr auto operator==(const Entity lhs, const tombstone_t rhs) noexcept + -> decltype(rhs.operator==(lhs)) { return rhs.operator==(lhs); } @@ -361,7 +373,8 @@ template * @return True if the two elements differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept { +[[nodiscard]] constexpr auto operator!=(const Entity lhs, const tombstone_t rhs) noexcept + -> decltype(rhs.operator==(lhs)) { return !(rhs == lhs); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 78533f05d..7c701356e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -233,6 +233,7 @@ SETUP_BASIC_TEST(utility entt/core/utility.cpp) SETUP_BASIC_TEST(component entt/entity/component.cpp) SETUP_BASIC_TEST(entity entt/entity/entity.cpp) +SETUP_BASIC_TEST(sfinae entt/entity/sfinae.cpp) SETUP_BASIC_TEST(group entt/entity/group.cpp) SETUP_BASIC_TEST(handle entt/entity/handle.cpp) SETUP_BASIC_TEST(helper entt/entity/helper.cpp) diff --git a/test/entt/entity/sfinae.cpp b/test/entt/entity/sfinae.cpp new file mode 100644 index 000000000..b0f0ab04f --- /dev/null +++ b/test/entt/entity/sfinae.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../../common/entity.h" + +template +inline constexpr bool is_equality_comparable_v = false; + +template +inline constexpr bool is_equality_comparable_v() == std::declval())>> = true; + +template +inline constexpr bool is_not_equality_comparable_v = false; + +template +inline constexpr bool is_not_equality_comparable_v() != std::declval())>> = true; + +template +inline constexpr bool is_comparable_v = is_equality_comparable_v + && is_equality_comparable_v + && is_not_equality_comparable_v + && is_not_equality_comparable_v; +struct unrelated {}; +struct use_my_operator {}; +template +bool operator==(use_my_operator, T &&); + +template +bool operator!=(use_my_operator, T &&); + +template +bool operator==(T &&, use_my_operator); + +template +bool operator!=(T &&, use_my_operator); + +struct entity_traits { + using value_type = test::entity; + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + static constexpr entity_type entity_mask = 0x3FFFF; // 18b + static constexpr entity_type version_mask = 0x0FFF; // 12b +}; + +struct other_entity_traits { + using value_type = test::other_entity; + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + static constexpr entity_type entity_mask = 0xFFFFFFFF; // 32b + static constexpr entity_type version_mask = 0x00; // 0b +}; + +template<> +struct entt::entt_traits: entt::basic_entt_traits { + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +template<> +struct entt::entt_traits: entt::basic_entt_traits { + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +TEST(Sfinae, NullComparison) { + static_assert(is_comparable_v); + static_assert(is_comparable_v); + static_assert(is_comparable_v); + static_assert(is_comparable_v); + + static_assert(is_comparable_v); + + static_assert(!is_comparable_v); +} + +TEST(Sfinae, TombstoneComparison) { + static_assert(is_comparable_v); + static_assert(is_comparable_v); + static_assert(is_comparable_v); + + static_assert(is_comparable_v); + + static_assert(!is_comparable_v); + + static_assert(!is_comparable_v); +}