aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/lib/include/newtype/concepts.hpp19
-rw-r--r--source/lib/include/newtype/impl/type_traits_extensions.hpp19
-rw-r--r--source/lib/include/newtype/newtype.hpp7
-rw-r--r--source/tests/src/hash.cpp19
4 files changed, 35 insertions, 29 deletions
diff --git a/source/lib/include/newtype/concepts.hpp b/source/lib/include/newtype/concepts.hpp
new file mode 100644
index 0000000..a29ba37
--- /dev/null
+++ b/source/lib/include/newtype/concepts.hpp
@@ -0,0 +1,19 @@
+#ifndef NEWTYPE_CONCEPTS_HPP
+#define NEWTYPE_CONCEPTS_HPP
+
+#include <concepts>
+#include <cstddef>
+#include <functional>
+
+namespace nt::concepts
+{
+ template<typename SubjectType>
+ concept hashable = requires(SubjectType subject) {
+ {
+ std::hash<SubjectType>{}(subject)
+ } -> std::convertible_to<std::size_t>;
+ };
+
+} // namespace nt::concepts
+
+#endif \ No newline at end of file
diff --git a/source/lib/include/newtype/impl/type_traits_extensions.hpp b/source/lib/include/newtype/impl/type_traits_extensions.hpp
index dc41649..bdd1cba 100644
--- a/source/lib/include/newtype/impl/type_traits_extensions.hpp
+++ b/source/lib/include/newtype/impl/type_traits_extensions.hpp
@@ -469,25 +469,6 @@ namespace nt::impl
} // namespace compound_arithmetic
- inline namespace std_support
- {
-
- template<typename T, typename = void>
- struct is_hashable : std::false_type
- {
- };
-
- template<typename T>
- struct is_hashable<T, std::void_t<decltype(std::declval<std::hash<T> const &>()(std::declval<T const &>()))>>
- : std::is_same<std::size_t, decltype(std::declval<std::hash<T> const &>()(std::declval<T const &>()))>
- {
- };
-
- template<typename T>
- auto constexpr is_hashable_v = is_hashable<T>::value;
-
- } // namespace std_support
-
inline namespace iterable_begin
{
template<typename T, typename = void>
diff --git a/source/lib/include/newtype/newtype.hpp b/source/lib/include/newtype/newtype.hpp
index 2e71553..b833c10 100644
--- a/source/lib/include/newtype/newtype.hpp
+++ b/source/lib/include/newtype/newtype.hpp
@@ -1,6 +1,7 @@
#ifndef NEWTYPE_NEWTYPE_HPP
#define NEWTYPE_NEWTYPE_HPP
+#include "newtype/concepts.hpp"
#include "newtype/derivable.hpp"
#include "newtype/deriving.hpp"
#include "newtype/impl/new_type_iterator_types.hpp"
@@ -535,12 +536,10 @@ namespace nt
namespace std
{
template<typename BaseType, typename TagType, auto DerivationClause>
+ requires(nt::concepts::hashable<BaseType> && DerivationClause(nt::Hash))
struct hash<nt::new_type<BaseType, TagType, DerivationClause>>
{
- template<typename BaseTypeT = BaseType>
- auto constexpr operator()(nt::new_type<BaseType, TagType, DerivationClause> const & object,
- std::enable_if_t<DerivationClause(nt::Hash) && nt::impl::is_hashable_v<BaseTypeT>> * = nullptr) const
- -> std::size_t
+ auto constexpr operator()(nt::new_type<BaseType, TagType, DerivationClause> const & object) const
{
return std::hash<BaseType>{}(object.decay());
}
diff --git a/source/tests/src/hash.cpp b/source/tests/src/hash.cpp
index e3f624f..bcf793c 100644
--- a/source/tests/src/hash.cpp
+++ b/source/tests/src/hash.cpp
@@ -1,31 +1,36 @@
+#include "newtype/concepts.hpp"
#include "newtype/derivable.hpp"
#include "newtype/deriving.hpp"
#include "newtype/impl/type_traits_extensions.hpp"
#include "newtype/newtype.hpp"
+#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>
+#include <string>
#include <unordered_map>
-SCENARIO("Hash", "[hash]")
+TEMPLATE_TEST_CASE("Hash", "[hash]", std::string, int)
{
GIVEN("A new_type not deriving nt::Hash")
{
- using type_alias = nt::new_type<int, struct tag>;
+ using type_alias = nt::new_type<TestType, struct tag>;
+ static_assert(nt::concepts::hashable<typename type_alias::base_type>);
THEN("it is not hashable")
{
- STATIC_REQUIRE_FALSE(nt::impl::is_hashable_v<type_alias>);
+ STATIC_REQUIRE_FALSE(nt::concepts::hashable<type_alias>);
}
}
GIVEN("A new_type over a hashable type deriving nt::Hash")
{
- using type_alias = nt::new_type<int, struct tag, deriving(nt::Hash)>;
+ using type_alias = nt::new_type<TestType, struct tag, deriving(nt::Hash)>;
+ static_assert(nt::concepts::hashable<typename type_alias::base_type>);
THEN("it is hashable")
{
- STATIC_REQUIRE(nt::impl::is_hashable_v<type_alias>);
+ STATIC_REQUIRE(nt::concepts::hashable<type_alias>);
}
}
@@ -35,16 +40,18 @@ SCENARIO("Hash", "[hash]")
{
};
using type_alias = nt::new_type<non_hashable, struct tag, deriving(nt::Hash)>;
+ static_assert(!nt::concepts::hashable<typename type_alias::base_type>);
THEN("it is not hashable")
{
- STATIC_REQUIRE_FALSE(nt::impl::is_hashable_v<type_alias>);
+ STATIC_REQUIRE_FALSE(nt::concepts::hashable<type_alias>);
}
}
GIVEN("A hashable new_type")
{
using type_alias = nt::new_type<int, struct tag, deriving(nt::Hash)>;
+ static_assert(nt::concepts::hashable<typename type_alias::base_type>);
THEN("it can be used a the key in an unordered_map")
{