-
Notifications
You must be signed in to change notification settings - Fork 1
/
Maybe.h
118 lines (100 loc) · 2.97 KB
/
Maybe.h
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
#pragma once
#include <new>
#include <utility>
#include <type_traits>
#include "throw.h"
template <typename T> class Maybe;
template <typename T, typename std::enable_if<
!std::is_reference<T>::value>::type* = nullptr> // rvalues only
static Maybe<T> Just(T &&x) { return Maybe<T>(std::forward<T>(x)); }
template <typename T>
static Maybe<T> Just(const T &x) { return Maybe<T>(x); }
template <typename T>
static Maybe<T> Nothing() { return Maybe<T>(); }
#define JUST_ELSE_NOTHING(P, X) \
((P) ? (X) : Nothing<decltype((X).x)>())
template <typename T>
class Maybe {
public:
typedef T value_type;
union {
T x;
};
Maybe() : hasValue(false) {}
explicit Maybe(T &&xa) : x(std::move(xa)), hasValue(true) {}
explicit Maybe(const T &xa) : x(xa), hasValue(true) {}
Maybe(const Maybe &other) : hasValue(other.hasValue) {
if (other.hasValue) new (&x) T(other.x);
}
Maybe(Maybe &&other) : hasValue(other.hasValue) { moveFrom(other); }
void operator=(const Maybe &other) {
if (hasValue) x.~T();
hasValue = other.hasValue;
if (other.hasValue) new (&x) T(other.x);
}
void operator=(Maybe &&other) {
if (hasValue) x.~T();
hasValue = other.hasValue;
moveFrom(other);
}
~Maybe() {
if (hasValue) x.~T();
hasValue = false;
}
bool isJust() const { return hasValue; }
bool isNothing() const { return !hasValue; }
const T orDefault(T y) const { return hasValue ? x : y; }
T& orDefault(T&& y) { return hasValue ? x : y; }
const T* orNull() const { return hasValue ? &x : nullptr; }
T* orNull() { return hasValue ? &x : nullptr; }
template <typename E, typename ...Args>
T &orThrow(Args... args) const {
if (hasValue) {
return x;
} else {
throw_<E>(std::forward<Args>(args)...);
}
}
template <typename ...Args>
T* orConstructDefault(Args... args) {
return hasValue ? &x : new T(args...);
}
template <typename F, typename G>
auto cond(F f, G g) const -> decltype(f(std::declval<T>())) {
return hasValue ? f(x) : g();
}
template <typename F>
auto bind(F f) const -> decltype(f(std::declval<T>())) {
return JUST_ELSE_NOTHING(hasValue, f(x));
}
template <typename F>
auto map(F f) const -> Maybe<decltype(f(std::declval<T>()))> {
return JUST_ELSE_NOTHING(hasValue, Just(f(x)));
}
private:
bool hasValue;
void moveFrom(Maybe &other) {
if (other.hasValue) {
new (&x) T(std::move(other.x));
other.hasValue = false;
other.x.~T();
}
}
};
template <typename A, typename B>
constexpr bool operator==(const Maybe<A> &a, const Maybe<B> &b) {
return a.isJust() && b.isJust()
? *a.orNull() == *b.orNull()
: a.isJust() == b.isJust();
}
template <typename A, typename B>
constexpr bool operator!=(const Maybe<A> &a, const Maybe<B> &b) {
return !(a == b);
}
template <typename M, typename K>
static auto findMaybe(const M &m, const K &k)
-> decltype(Just(m.find(k)->second))
{
auto it = m.find(k);
return JUST_ELSE_NOTHING(it != m.end(), Just(it->second));
}