-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmaybe.hpp
135 lines (104 loc) · 2.56 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
#ifndef SLAP_MAYBE_HPP
#define SLAP_MAYBE_HPP
#include <type_traits>
#include <utility>
#include <stdexcept>
// maybe monad
template<class T> class maybe;
namespace detail {
// ensures T is a maybe<U>
template<class T>
struct require_maybe;
template<class T>
struct require_maybe< maybe<T> > {
using type = maybe<T>;
};
}
struct none { };
template<class T>
class maybe {
typename std::aligned_union<0, T>::type storage;
bool set;
public:
using value_type = T;
// monadic return (none/just)
maybe(none = {}) : set(false) { }
maybe(const T& value) : set(true) { new (&storage) T(value); }
maybe(T&& value) : set(true) { new (&storage) T(std::move(value)); }
maybe(maybe&& other) : set(other.set) {
if(set) new (&storage) T(std::move(other.get()));
// note: moved from gets unset
other.set = false;
}
maybe(const maybe& other) : set(other.set) {
if(set) new (&storage) T(other.get());
}
// assignment
maybe& operator=(maybe&& other) {
if(set == other.set) {
if(set) {
get() = std::move(other.get());
}
} else {
if(set) {
get().~T();
} else {
new (&storage) T(std::move(other.get()));
}
set = other.set;
}
// note: moved from gets unset
other.set = false;
return *this;
}
maybe& operator=(const maybe& other) {
if(set == other.set) {
if(set) {
get() = other.get();
}
} else {
if(set) {
get().~T();
} else {
new (&storage) T(other.get());
}
set = other.get;
}
return *this;
}
// monadic bind
template<class F>
using bind_type = typename std::result_of<F (const T& )>::type;
template<class F>
typename detail::require_maybe< bind_type<F> >::type
operator>>(const F& f) const {
if(!set) return {};
return f(get());
}
// TODO bind from temporary
~maybe() {
if(set) get().~T();
}
// monad escape
explicit operator bool () const { return set; }
const T& get() const {
if(!set) throw std::runtime_error("accessing none");
return *reinterpret_cast<const T*>(&storage);
}
T& get() {
if(!set) throw std::runtime_error("accessing none");
return *reinterpret_cast<T*>(&storage);
}
};
// convenience constructors
template<class T>
static maybe<typename std::decay<T>::type> just(T&& value) {
return {std::forward<T>(value)};
}
// functor map
template<class T, class F>
static maybe<typename std::result_of<F(T)>::type> map(const maybe<T>& x, const F& f) {
if(x) return f(x.get());
return {};
}
#endif