Zen C++ Libraries
Zero-dependency re-usable components for C++
Loading...
Searching...
No Matches
either.hpp
Go to the documentation of this file.
1
93
94#ifndef ZEN_EITHER_HPP
95#define ZEN_EITHER_HPP
96
97#include <condition_variable>
98#include <type_traits>
99#include <utility>
100
101#include "zen/config.hpp"
102#include "zen/formatting.hpp"
103
104ZEN_NAMESPACE_START
105
106struct dummy {};
107
109template<typename L>
110struct left_t {
111 using storage_t = std::conditional_t<std::is_void_v<L>, dummy, L>;
112 storage_t value;
113};
114
116template<typename R>
117struct right_t {
118 using storage_t = std::conditional_t<std::is_void_v<R>, dummy, R>;
119 storage_t value;
120};
121
123template<typename L, typename R>
124class either {
125
126 template<typename L2, typename R2>
127 friend class either;
128
129 using left_storage_t = std::conditional_t<std::is_void_v<L>, dummy, L>;
130 using right_storage_t = std::conditional_t<std::is_void_v<R>, dummy, R>;
131
132 union {
133 left_storage_t left_value;
134 right_storage_t right_value;
135 };
136
137 bool has_right_v;
138
139public:
140
142 template<typename L2>
143 inline either(left_t<L2>&& value): left_value(std::move(value.value)), has_right_v(false) {};
144
146 template<typename R2>
147 inline either(right_t<R2>&& value): right_value(std::move(value.value)), has_right_v(true) {};
148
149 either(either &&other): has_right_v(std::move(other.has_right_v)) {
150 if (has_right_v) {
151 new(&right_value)R(std::move(other.right_value));
152 } else {
153 new(&left_value)L(std::move(other.left_value));
154 }
155 }
156
157 either(const either &other): has_right_v(other.has_right_v) {
158 if (has_right_v) {
159 new(&right_value)R(other.right_value);
160 } else {
161 new(&left_value)L(other.left_value);
162 }
163 }
164
165 template<typename L2, typename R2>
166 either(either<L2, R2>&& other): has_right_v(std::move(other.has_right_v)) {
167 if (has_right_v) {
168 new(&right_value)R(std::move(other.right_value));
169 } else {
170 new(&left_value)L(std::move(other.left_value));
171 }
172 }
173
174 template<typename L2, typename R2>
175 either(const either<L2, R2> &other): has_right_v(other.has_right_v) {
176 if (has_right_v) {
177 new(&right_value)R(other.right_value);
178 } else {
179 new(&left_value)L(other.left_value);
180 }
181 }
182
183 either<L, R>& operator=(const either<L, R>& other) {
184 if (has_right_v) {
185 new(&right_value)R(other.right_value);
186 } else {
187 new(&left_value)L(other.left_value);
188 }
189 return *this;
190 }
191
192 either<L, R>& operator=(either<L, R>&& other) {
193 if (has_right_v) {
194 new(&right_value)R(std::move(other.right_value));
195 } else {
196 new(&left_value)L(std::move(other.left_value));
197 }
198 return *this;
199 }
200
201 R* operator->() {
202 ZEN_ASSERT(has_right_v);
203 return &right_value;
204 }
205
206 template<typename T = R>
207 T &operator*() requires (!std::same_as<T, void>) {
208 ZEN_ASSERT(has_right_v);
209 return right_value;
210 }
211
218 bool is_left() const { return !has_right_v; }
219
226 bool is_right() const { return has_right_v; }
227
228 operator bool() const {
229 return is_right();
230 }
231
232 R unwrap() requires (has_display<L>) {
233 if (!has_right_v) {
234 ZEN_PANIC("error: %s", display(left_value).c_str());
235 }
236 return right_value;
237 }
238
239 R unwrap() requires (!has_display<L>) {
240 if (!has_right_v) {
241 ZEN_PANIC("trying to unwrap a zen::either which is left-valued");
242 }
243 return right_value;
244 }
245
246 L unwrap_left() {
247 if (has_right_v) {
248 ZEN_PANIC("trying to unwrap the left side a zen::either which is right-valued");
249 }
250 return left_value;
251 }
252
254 template<typename T = L>
255 T &left() requires (!std::same_as<void, T>) {
256 ZEN_ASSERT(!has_right_v);
257 return left_value;
258 }
259
261 template<typename T = R>
262 T &right() requires (!std::same_as<T, void>) {
263 ZEN_ASSERT(has_right_v);
264 return right_value;
265 }
266
269 L take_left() && {
270 ZEN_ASSERT(!has_right_v);
271 return std::move(left_value);
272 }
273
276 R take_right() && {
277 ZEN_ASSERT(has_right_v);
278 return std::move(right_value);
279 }
280
281 ~either() {
282 if (has_right_v) {
283 right_value.~right_storage_t();
284 } else {
285 left_value.~left_storage_t();
286 }
287 }
288
289};
290
297inline left_t<void> left() {
298 return left_t<void> {};
299}
300
307template<typename L>
308left_t<L&> left(L& value) {
309 return left_t<L&> { value };
310}
311
318template<typename L>
319left_t<L> left(L&& value) {
320 return left_t<L> { std::move(value) };
321}
322
329inline right_t<void> right() {
330 return right_t<void> {};
331}
332
340template<typename R>
341right_t<R&> right(R& value) {
342 return right_t<R&> { value };
343}
344
352template<typename R>
353right_t<R> right(R&& value) {
354 return right_t<R> { std::move(value) };
355}
356
364#define ZEN_TRY(value) \
365 if (value.is_left()) { \
366 return ::zen::left(std::move(value).take_left()); \
367 }
368
370#define ZEN_TRY_DISCARD(expr) \
371 { \
372 auto zen__either__result = (expr); \
373 if (zen__either__result.is_left()) { \
374 return ::zen::left(std::move(zen__either__result.left())); \
375 } \
376 }
377
378ZEN_NAMESPACE_END
379
380#endif // ZEN_EITHER_HPP
A type for computations that may fail.
Definition either.hpp:124
T & right()
Get a reference to the right-sided value of this either type.
Definition either.hpp:262
R take_right() &&
Definition either.hpp:276
bool is_left() const
Definition either.hpp:218
T & left()
Get a reference to the left-sided value of this either type.
Definition either.hpp:255
L take_left() &&
Definition either.hpp:269
bool is_right() const
Definition either.hpp:226
Definition value.hpp:34
right_t< void > right()
Definition either.hpp:329
left_t< void > left()
Definition either.hpp:297
Definition either.hpp:106