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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#ifndef AIRMAP_OUTCOME_H_
#define AIRMAP_OUTCOME_H_
#include <type_traits>
namespace airmap {
/// Outcome models a return value from a function XOR an error object
/// describing the error condition if no value can be returned.
template <typename Value, typename Error>
class Outcome {
public:
/// @cond
static_assert(not std::is_same<Value, Error>::value, "Value and Error must not be the same type");
static_assert(std::is_copy_constructible<Value>::value && std::is_move_constructible<Value>::value,
"Value must be copy- and move-constructible");
static_assert(std::is_copy_constructible<Error>::value && std::is_move_constructible<Error>::value,
"Error must be copy- and move-constructible");
/// @endcond
/// Outcome initializes a new instance with value.
explicit Outcome(const Value& value) : type{Type::value} {
new (&data.value) Value{value};
}
/// Outcome initializes a new instance with error.
explicit Outcome(const Error& error) : type{Type::error} {
new (&data.error) Error{error};
}
/// Outcome initializes a new instance with the value or error of 'other'.
Outcome(const Outcome& other) : type{other.type} {
switch (type) {
case Type::error:
new (&data.error) Error{other.data.error};
break;
case Type::value:
new (&data.value) Value{other.data.value};
break;
}
}
/// Outcome initializes a new instance with the value or error of 'other'.
Outcome(Outcome&& other) : type{other.type} {
switch (type) {
case Type::error:
new (&data.error) Error{other.data.error};
break;
case Type::value:
new (&data.value) Value{other.data.value};
break;
}
}
/// Outcome assigns the value or error contained in 'other' to this instance.
Outcome& operator=(const Outcome& other) {
switch (type) {
case Type::error: {
(&data.error)->~Error();
break;
}
case Type::value: {
(&data.value)->~Value();
break;
}
}
type = other.type;
switch (type) {
case Type::error: {
new (&data.error) Error{other.data.error};
break;
}
case Type::value: {
new (&data.value) Value{other.data.value};
break;
}
}
return *this;
}
/// Outcome assigns the value or error contained in 'other' to this instance.
Outcome& operator=(Outcome&& other) {
switch (type) {
case Type::error: {
(&data.error)->~Error();
break;
}
case Type::value: {
(&data.value)->~Value();
break;
}
}
type = other.type;
switch (type) {
case Type::error: {
new (&data.error) Error{other.data.error};
break;
}
case Type::value: {
new (&data.value) Value{other.data.value};
break;
}
}
return *this;
}
/// ~Outcome frees up the contained error or value contained in this instance.
~Outcome() {
switch (type) {
case Type::error:
data.error.~Error();
break;
case Type::value:
data.value.~Value();
break;
}
}
/// operator bool returns true if a value is contained in this instance.
explicit operator bool() const {
return !has_error();
}
/// has_error returns true if this instance carries an error.
inline bool has_error() const {
return type == Type::error;
}
/// has_value returns true if this instance carries a value.
inline bool has_value() const {
return type == Type::value;
}
/// error returns an immutable reference to the Error contained in this instance.
/// The result of this call is undefined if has_error() returns false.
inline const Error& error() const {
return data.error;
}
/// value returns an immutable reference to the Value contained in this instance.
/// The result of this call is undefined if has_value() returns false.
inline const Value& value() const {
return data.value;
}
private:
enum class Type { value, error };
Type type;
union Data {
Data() : value{} {
}
~Data() {
}
Value value;
Error error;
} data;
};
} // namespace airmap
#endif // AIRMAP_OUTCOME_H_