-
Notifications
You must be signed in to change notification settings - Fork 18
/
maybe.hpp
140 lines (105 loc) · 2.83 KB
/
maybe.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#ifndef NEITHER_MAYBE_HPP
#define NEITHER_MAYBE_HPP
#include <memory>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <neither/traits.hpp>
namespace neither {
template <class T> struct Maybe;
template <> struct Maybe<void> {};
template <class T> struct Maybe {
using size_type = std::size_t;
union {
T value;
};
bool const hasValue;
constexpr Maybe() : hasValue{false} {}
constexpr Maybe(T const& value) : value{value}, hasValue{true} {}
constexpr Maybe(T&& value) : value{std::move(value)}, hasValue{true} {}
constexpr Maybe(Maybe<void>) : hasValue{false} {}
constexpr Maybe(Maybe<T> const &o) : hasValue{o.hasValue} {
if (o.hasValue) {
new (&value)T(o.value);
}
}
~Maybe() {
if (hasValue) {
value.~T();
}
}
constexpr T get(T defaultValue) {
return hasValue ? value : defaultValue;
}
constexpr T unsafeGet() {
assert(hasValue && "unsafeGet must not be called on an empty Maybe");
return value;
}
constexpr size_type size() const noexcept { return hasValue ? 1: 0; }
constexpr bool empty() const noexcept { return !hasValue; }
template<class F>
constexpr auto map(F const &f) const&
-> Maybe<decltype(f(isCopyable(value)))> {
using ReturnType = decltype(f(value));
if (!hasValue) {
return Maybe<ReturnType>();
}
return Maybe<ReturnType>(f(value));
}
template<class F>
auto map(F const& f)&&
-> Maybe<decltype(f(std::move(value)))> {
using ReturnType = decltype(f(std::move(value)));
if (!hasValue) {
return Maybe<ReturnType>();
}
return Maybe<ReturnType>(f(std::move(value)));
}
template <class F>
constexpr auto flatMap(F const& f) const&
-> decltype(ensureMaybe(f(value))) {
using ReturnType = decltype(f(value));
if (!hasValue) {
return ReturnType();
}
return f(value);
}
template <class F>
constexpr auto flatMap(F const& f)&&
-> decltype(ensureMaybe(f(std::move(value)))) {
using ReturnType = decltype(f(std::move(value)));
if (!hasValue) {
return ReturnType();
}
return f(std::move(value));
}
constexpr operator bool() const { return hasValue; }
};
template <typename T>
auto maybe(T value) -> Maybe<T> { return {value}; }
template <typename T = void>
auto maybe() -> Maybe<T> { return {}; }
namespace {
inline
bool equal(Maybe<void> const&, Maybe<void> const&) {
return true;
}
template <typename T>
bool equal(Maybe<T> const &a, Maybe<T> const &b) {
if (a.hasValue) {
return b.hasValue && a.value == b.value;
}
return !b.hasValue;
}
}
template <typename T>
bool operator == (Maybe<T> const& a, Maybe<T> const& b) {
return equal(a, b);
}
template <typename T>
bool operator != (Maybe<T> const& a, Maybe<T> const& b) {
return !(a == b);
}
static const auto none = maybe();
}
#endif