Skip to content

Commit 863a89c

Browse files
authored
Merge pull request bemanproject#69 from steve-downey/hash
Implement std::hash for beman::optional
2 parents 105f209 + b2e6f6c commit 863a89c

File tree

2 files changed

+51
-11
lines changed

2 files changed

+51
-11
lines changed

include/beman/optional26/optional.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,4 +1147,21 @@ class optional<T&> {
11471147

11481148
} // namespace beman::optional26
11491149

1150+
namespace std {
1151+
template <typename T>
1152+
requires requires(T a) {
1153+
{ std::hash<remove_const_t<T>>{}(a) } -> std::convertible_to<std::size_t>;
1154+
}
1155+
struct hash<beman::optional26::optional<T>> {
1156+
static_assert(!is_reference_v<T>, "hash is not enabled for reference types");
1157+
size_t operator()(const beman::optional26::optional<T>& o) const
1158+
noexcept(noexcept(hash<remove_const_t<T>>{}(*o))) {
1159+
if (o) {
1160+
return std::hash<std::remove_const_t<T>>{}(*o);
1161+
} else {
1162+
return 0;
1163+
}
1164+
}
1165+
};
1166+
} // namespace std
11501167
#endif // BEMAN_OPTIONAL26_OPTIONAL_HPP

src/beman/optional26/tests/optional.t.cpp

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <functional>
1111
#include <ranges>
1212
#include <tuple>
13+
#include <algorithm>
1314

1415
#include <gtest/gtest.h>
1516

@@ -596,7 +597,7 @@ TEST(OptionalTest, RangeTest) {
596597
}
597598
}
598599

599-
TEST(ViewMaybeTest, Constructors) {
600+
TEST(OptionalTest, RangeConstructors) {
600601
std::ranges::single_view<std::optional<int>> s;
601602
std::ranges::single_view<std::optional<int>> s2{s};
602603
std::ranges::single_view<std::optional<int>> s3{std::optional<int>{}};
@@ -610,7 +611,7 @@ TEST(ViewMaybeTest, Constructors) {
610611
std::ignore = n3;
611612
}
612613

613-
TEST(ViewMaybeTest, ConceptCheckRef) {
614+
TEST(OptionalTest, ConceptCheckRef) {
614615
static_assert(std::ranges::range<beman::optional26::optional<int&>>);
615616
static_assert(std::ranges::view<beman::optional26::optional<int&>>);
616617
static_assert(std::ranges::input_range<beman::optional26::optional<int&>>);
@@ -646,7 +647,7 @@ TEST(ViewMaybeTest, ConceptCheckRef) {
646647
static_assert(std::ranges::random_access_range<beman::optional26::optional<ref>>);
647648
}
648649

649-
TEST(ViewMaybeTest, BreathingTest) {
650+
TEST(OptionalTest, BreathingTest) {
650651
beman::optional26::optional<int> m;
651652
beman::optional26::optional<int> m1{1};
652653

@@ -664,7 +665,7 @@ TEST(ViewMaybeTest, BreathingTest) {
664665
ASSERT_EQ(*std::begin(d0), 1.0);
665666
}
666667

667-
TEST(ViewMaybeTest, BreathingTestRef) {
668+
TEST(OptionalTest, BreathingTestRef) {
668669
beman::optional26::optional<int&> m;
669670

670671
int one = 1;
@@ -683,7 +684,7 @@ TEST(ViewMaybeTest, BreathingTestRef) {
683684
ASSERT_EQ(*std::begin(d0), 1.0);
684685
}
685686

686-
TEST(ViewMaybe, CompTest) {
687+
TEST(OptionalTest, CompTest) {
687688
beman::optional26::optional<int> m;
688689
beman::optional26::optional<int> m0{0};
689690
beman::optional26::optional<int> m1{1};
@@ -703,7 +704,7 @@ TEST(ViewMaybe, CompTest) {
703704
ASSERT_TRUE(m1 <= m1a);
704705
}
705706

706-
TEST(ViewMaybe, CompTestRef) {
707+
TEST(OptionalTest, CompTestRef) {
707708
beman::optional26::optional<int&> m;
708709
int zero = 0;
709710
int one = 1;
@@ -743,7 +744,7 @@ inline constexpr auto yield_if = []<class T>(bool b, T x) {
743744
return b ? beman::optional26::optional<T>{std::move(x)} : beman::optional26::nullopt;
744745
};
745746

746-
TEST(ViewMaybeTest, PythTripleTest) {
747+
TEST(OptionalTest, PythTripleTest) {
747748
using std::views::iota;
748749
auto triples = and_then(iota(1), [](int z) {
749750
return and_then(iota(1, z + 1), [=](int x) {
@@ -757,7 +758,7 @@ TEST(ViewMaybeTest, PythTripleTest) {
757758

758759
using namespace beman;
759760

760-
TEST(ViewMaybeTest, ValueBase) {
761+
TEST(OptionalTest, ValueBase) {
761762
int i = 7;
762763
beman::optional26::optional<int> v1{};
763764

@@ -769,7 +770,7 @@ TEST(ViewMaybeTest, ValueBase) {
769770
ASSERT_EQ(i, 7);
770771
}
771772

772-
TEST(ViewMaybeTest, RefWrapper) {
773+
TEST(OptionalTest, RefWrapper) {
773774
int i = 7;
774775

775776
beman::optional26::optional<int> v2{std::ref(i)};
@@ -778,7 +779,7 @@ TEST(ViewMaybeTest, RefWrapper) {
778779
ASSERT_EQ(i, 7);
779780
}
780781

781-
TEST(ViewMaybeTest, ValueNonDefaultConstruct) {
782+
TEST(OptionalTest, ValueNonDefaultConstruct) {
782783
using beman::optional26::tests::int_ctor;
783784
int_ctor i = 7;
784785
beman::optional26::optional<int_ctor> v1{};
@@ -787,7 +788,7 @@ TEST(ViewMaybeTest, ValueNonDefaultConstruct) {
787788
std::ignore = v2;
788789
}
789790

790-
TEST(ViewMaybeTest, RefBase) {
791+
TEST(OptionalTest, RefBase) {
791792
int i = 7;
792793
beman::optional26::optional<int&> v1{};
793794
// ASSERT_TRUE(v1.size() == 0);
@@ -820,3 +821,25 @@ TEST(ViewMaybeTest, RefBase) {
820821
}
821822
ASSERT_EQ(s, 9);
822823
}
824+
825+
TEST(OptionalTest, HashTest) {
826+
beman::optional26::optional<int> o1 = beman::optional26::nullopt;
827+
beman::optional26::optional<int> o2 = beman::optional26::nullopt;
828+
beman::optional26::optional<int> o3 = 42;
829+
beman::optional26::optional<int> o4 = 42;
830+
831+
auto h1 = std::hash<beman::optional26::optional<int>>{}(o1);
832+
auto h2 = std::hash<beman::optional26::optional<int>>{}(o2);
833+
auto h3 = std::hash<beman::optional26::optional<int>>{}(o3);
834+
auto h4 = std::hash<beman::optional26::optional<int>>{}(o4);
835+
836+
EXPECT_EQ(h1, h2);
837+
EXPECT_EQ(h3, h4);
838+
EXPECT_NE(h1, h3);
839+
840+
for(int i : std::views::iota(0, 1000)) {
841+
auto h1 = std::hash<beman::optional26::optional<int>>{}(i);
842+
auto h2 = std::hash<int>{}(i);
843+
EXPECT_EQ(h1, h2);
844+
}
845+
}

0 commit comments

Comments
 (0)