Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions src/entt/entity/entity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
};

template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
struct entt_traits<Type, std::void_t<typename Type::entity_type>>
: entt_traits<typename Type::entity_type> {
using value_type = Type;
};
Expand Down Expand Up @@ -57,8 +57,12 @@ struct entt_traits<std::uint64_t> {
* @brief Common basic entity traits implementation.
* @tparam Traits Actual entity traits to use.
*/

template<typename Traits, typename = void>
class basic_entt_traits;

template<typename Traits>
class basic_entt_traits {
class basic_entt_traits<Traits, std::void_t<decltype(sizeof(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");
Expand Down Expand Up @@ -159,8 +163,11 @@ class basic_entt_traits {
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type, typename = void>
struct entt_traits;

template<typename Type>
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
struct entt_traits<Type, std::void_t<decltype(sizeof(basic_entt_traits<internal::entt_traits<Type>>))>>: basic_entt_traits<internal::entt_traits<Type>> {
/*! @brief Base type. */
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
Expand Down Expand Up @@ -207,7 +214,7 @@ struct null_t {
* @tparam Entity Type of identifier.
* @return The null representation for the given type.
*/
template<typename Entity>
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
[[nodiscard]] constexpr operator Entity() const noexcept {
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
Expand Down Expand Up @@ -238,7 +245,7 @@ struct null_t {
* @param entity Identifier with which to compare.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
Expand All @@ -251,7 +258,8 @@ struct null_t {
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[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);
}
};
Expand All @@ -264,7 +272,8 @@ struct null_t {
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[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);
}

Expand All @@ -276,7 +285,8 @@ template<typename Entity>
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[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);
}

Expand All @@ -287,7 +297,7 @@ struct tombstone_t {
* @tparam Entity Type of identifier.
* @return The tombstone representation for the given type.
*/
template<typename Entity>
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
[[nodiscard]] constexpr operator Entity() const noexcept {
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
Expand Down Expand Up @@ -318,7 +328,7 @@ struct tombstone_t {
* @param entity Identifier with which to compare.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
template<typename Entity, decltype(sizeof(entt_traits<Entity>)) * = nullptr>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;

Expand All @@ -336,8 +346,9 @@ struct tombstone_t {
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[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);
}
};

Expand All @@ -349,7 +360,8 @@ struct tombstone_t {
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[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);
}

Expand All @@ -361,7 +373,8 @@ template<typename Entity>
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[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);
}

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,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)
Expand Down
88 changes: 88 additions & 0 deletions test/entt/entity/sfinae.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <entt/config/config.h>
#include <entt/entity/entity.hpp>
#include "../../common/entity.h"

template<typename T, typename U = T, typename = void>
inline constexpr bool is_equality_comparable_v = false;

template<typename T, typename U>
inline constexpr bool is_equality_comparable_v<T, U, std::void_t<decltype(std::declval<T>() == std::declval<U>())>> = true;

template<typename T, typename U = T, typename = void>
inline constexpr bool is_not_equality_comparable_v = false;

template<typename T, typename U>
inline constexpr bool is_not_equality_comparable_v<T, U, std::void_t<decltype(std::declval<T>() != std::declval<U>())>> = true;

template<typename T, typename U = T>
inline constexpr bool is_comparable_v = is_equality_comparable_v<T, U>
&& is_equality_comparable_v<U, T>
&& is_not_equality_comparable_v<T, U>
&& is_not_equality_comparable_v<U, T>;
struct unrelated {};
struct use_my_operator {};
template<typename T>
bool operator==(use_my_operator, T &&);

template<typename T>
bool operator!=(use_my_operator, T &&);

template<typename T>
bool operator==(T &&, use_my_operator);

template<typename T>
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<test::entity>: entt::basic_entt_traits<entity_traits> {
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
};

template<>
struct entt::entt_traits<test::other_entity>: entt::basic_entt_traits<other_entity_traits> {
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
};

TEST(Sfinae, NullComparison) {
static_assert(is_comparable_v<entt::null_t>);
static_assert(is_comparable_v<entt::null_t, entt::entity>);
static_assert(is_comparable_v<entt::null_t, test::entity>);
static_assert(is_comparable_v<entt::null_t, test::other_entity>);

static_assert(is_comparable_v<use_my_operator, entt::null_t>);

static_assert(!is_comparable_v<entt::null_t, unrelated>);
}

TEST(Sfinae, TombstoneComparison) {
static_assert(is_comparable_v<entt::tombstone_t>);
static_assert(is_comparable_v<entt::tombstone_t, test::entity>);
static_assert(is_comparable_v<entt::tombstone_t, test::other_entity>);

static_assert(is_comparable_v<use_my_operator, entt::tombstone_t>);

static_assert(!is_comparable_v<entt::tombstone_t, unrelated>);

static_assert(!is_comparable_v<entt::tombstone_t, entt::null_t>);
}