mitteLib
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1//
2// Created by Pietro Saccardi on 22/12/2020.
3//
4
5#ifndef MLAB_RESULT_HPP
6#define MLAB_RESULT_HPP
7
8#include <memory>
9#include <mlab/any_of.hpp>
10#include <type_traits>
11#include <vector>
12
13namespace mlab {
14
15 enum struct result_content {
16 error,
17 data
18 };
19
29 template <class... Args>
30 class result;
31
32 namespace traits {
33 template <class T>
34 struct is_result : std::false_type {};
35 template <class... Args>
36 struct is_result<mlab::result<Args...>> : std::true_type {};
37 }// namespace traits
38
39 template <class T>
40 concept is_result = traits::is_result<std::decay_t<T>>::value;
41
42
52 template <is_result R1, is_result... Rs>
53 [[nodiscard]] decltype(auto) concat_result(R1 &&r1, Rs &&...rs);
54
56 template <class E, class T>
57 inline bool operator==(result<E, T> const &res) const;
58
59 template <class E, class T>
60 inline bool operator!=(result<E, T> const &res) const;
61 };
62
64
69 template <class E, class T>
70 struct result_impl {
71 template <result_content RC>
72 struct content_wrap {
73 using content_type = typename std::conditional<RC == result_content::error, E, T>::type;
75 };
76 };
77
78 template <class E, class T>
79 class result<E, T> : private any_of<result_content, result_impl<E, T>::template content_wrap, result_content::error> {
80 template <result_content RC>
82
84
85 public:
86 result() = default;
87 result(result &&) noexcept = default;
88
89 using error_type = E;
90 using value_type = T;
91 static constexpr std::size_t value_size = 1;
92 using value_type_as_tuple = std::tuple<T>;
93
94 inline result(result const &other);
95
96 inline result(E error);
97
98 inline result(T data);
99
100 result &operator=(E error);
101
102 result &operator=(T data);
103
104 result &operator=(result &&) noexcept = default;
105
106 result &operator=(result const &other);
107
108 [[nodiscard]] inline T &operator*();
109
110 [[nodiscard]] inline T const &operator*() const;
111
112 [[nodiscard]] inline T *operator->();
113
114 [[nodiscard]] inline T const *operator->() const;
115
116 [[nodiscard]] T const &release() const &;
117
118 [[nodiscard]] T &release() &;
119
120 [[nodiscard]] T &&release() &&;
121
122 inline explicit operator bool() const;
123
124 using base::type;
125
126 [[nodiscard]] inline E error() const;
127 };
128
129 namespace impl {
130 template <class... Args>
132 using type = std::tuple<Args...>;
133 };
134
135 template <class T1, class T2>
136 struct tuple_container_type<T1, T2> {
137 using type = std::pair<T1, T2>;
138 };
139
140 template <class T>
142
143 template <>
145 }// namespace impl
146
147 template <class... Ts>
148 using tuple_container_t = typename impl::tuple_container_type<Ts...>::type;
149
150 template <class E, class... Ts>
151 class result<E, Ts...> : private result<E, tuple_container_t<Ts...>> {
152 public:
153 using base = result<E, tuple_container_t<Ts...>>;
154
155 using typename base::error_type;
156 using typename base::value_type;
157 static constexpr std::size_t value_size = sizeof...(Ts);
158 using value_type_as_tuple = std::tuple<Ts...>;
159
160 result() = default;
161 result(result &&) noexcept = default;
162 result(result const &other) = default;
163 result &operator=(result const &other) = default;
164 result &operator=(result &&other) = default;
165
166 inline result(Ts... ts);
167 inline result(base b);
168
169 using base::base;
170 using base::error;
171 using base::type;
172 using base::operator=;
173 using base::operator*;
174 using base::release;
175 using base::operator->;
176 using base::operator bool;
177 };
178
179 template <class E>
180 class result<E, void> : public result<E, result_success_type> {
181 public:
183 using typename base::error_type;
184 using typename base::value_type;
185 static constexpr std::size_t value_size = 0;
186 using value_type_as_tuple = std::tuple<>;
187
188 template <class T, class = typename std::enable_if<// Disable copies of the same copy constructor
189 not std::is_void<T>::value and not std::is_same<T, result_success_type>::value>::type>
190 inline result(result<E, T> const &other);
191
192 result() = default;
193 result(result &&) noexcept = default;
194 result(result const &other) = default;
195 result &operator=(result const &other) = default;
196 result &operator=(result &&other) = default;
197
198 using base::base;
199 using base::operator=;
200 using base::error;
201 using base::type;
202 using base::operator bool;
203 };
204
205 template <class E>
206 class result<E> : public result<E, void> {
207 public:
209 using base::value_size;
210 using typename base::error_type;
211 using typename base::value_type;
212 using typename base::value_type_as_tuple;
213
214 result() = default;
215 result(result &&) noexcept = default;
216 result(result const &other) = default;
217 result &operator=(result const &other) = default;
218 result &operator=(result &&other) = default;
219
220 using base::base;
221 using base::operator=;
222 using base::error;
223 using base::type;
224 using base::operator bool;
225 };
226
227 template <class T, is_result R>
228 [[nodiscard]] decltype(auto) get(R &&r);
229
230 template <std::size_t I, is_result R>
231 [[nodiscard]] decltype(auto) get(R &&r);
232
233 template <is_result R>
234 [[nodiscard]] auto result_to_tuple(R &&r);
235
236 template <class E, class Tuple>
237 [[nodiscard]] auto result_from_tuple(Tuple &&tpl);
238}// namespace mlab
239
240namespace mlab {
241
242 template <class T, is_result R>
243 decltype(auto) get(R &&r) {
244 if constexpr (std::decay_t<R>::value_size == 1) {
245 if constexpr (std::is_rvalue_reference_v<R &&>) {
246 // Move the content
247 return std::move(*r);
248 } else {
249 return *r;
250 }
251 } else {
252 return std::get<T>(*r);
253 }
254 }
255
256 template <std::size_t I, is_result R>
257 decltype(auto) get(R &&r) {
258 if constexpr (std::decay_t<R>::value_size == 1) {
259 static_assert(I == 0);
260 if constexpr (std::is_rvalue_reference_v<R &&>) {
261 // Move the content
262 return std::move(*r);
263 } else {
264 return *r;
265 }
266 } else {
267 return std::get<I>(*r);
268 }
269 }
270
271 template <class E, class T>
273 base::template set<result_content::error>(content_wrap<result_content::error>{std::move(error)});
274 return *this;
275 }
276
277 template <class E, class T>
279 base::template set<result_content::data>(content_wrap<result_content::data>{std::move(data)});
280 return *this;
281 }
282
283 template <class E, class T>
284 result<E, T>::result(result<E, T> const &other) : base{other.type()} {
285 *this = other;
286 }
287
288 template <class E, class T>
289 result<E, T> &result<E, T>::operator=(result<E, T> const &other) {
290 if (&other != this) {
291 switch (other.type()) {
293 base::template set<result_content::error>(content_wrap<result_content::error>{other.error()});
294 break;
296 base::template set<result_content::data>(content_wrap<result_content::data>{*other});
297 break;
298 }
299 }
300 return *this;
301 }
302
303 template <class E, class T>
305 return type() == result_content::data;
306 }
307
308 template <class E, class T>
310 return base::template get<result_content::data>().content;
311 }
312
313 template <class E, class T>
314 T const &result<E, T>::release() const & {
315 return **this;
316 }
317
318 template <class E, class T>
320 return **this;
321 }
322
323 template <class E, class T>
325 return std::move(**this);
326 }
327
328
329 template <class E, class T>
330 T const &result<E, T>::operator*() const {
331 return base::template get<result_content::data>().content;
332 }
333
334 template <class E, class T>
336 return &base::template get<result_content::data>().content;
337 }
338
339 template <class E, class T>
340 T const *result<E, T>::operator->() const {
341 return &base::template get<result_content::data>().content;
342 }
343
344 template <class E, class T>
346 return base::template get<result_content::error>().content;
347 }
348
349 template <class E, class T>
351
352 template <class E, class T>
354
355 template <class E, class T>
356 bool result_success_type::operator==(result<E, T> const &res) const { return bool(res); }
357
358 template <class E, class T>
359 bool result_success_type::operator!=(result<E, T> const &res) const { return not bool(res); }
360
361 template <class E>
362 template <class T, class>
364 if (other.type() == result_content::error) {
365 *this = other.error();
366 } else {
367 *this = result_success;
368 }
369 }
370
371 template <class E, class... Ts>
372 result<E, Ts...>::result(Ts... ts) : base{tuple_container_t<Ts...>(std::forward<Ts>(ts)...)} {}
373
374 template <class E, class... Ts>
375 result<E, Ts...>::result(result<E, Ts...>::base b) : base{std::forward<base>(b)} {}
376
377 namespace impl {
378 template <std::size_t... Is, is_result R>
379 [[nodiscard]] auto result_to_tuple(std::index_sequence<Is...>, R &&r) {
380 return std::make_tuple(mlab::get<Is>(r)...);
381 }
382
383 template <class, class>
385
386 template <class E, class... Args>
387 struct tuple_to_result<E, std::tuple<Args...>> {
388 using type = result<E, Args...>;
389 };
390
391 template <class>
393
394 template <class E, class... Args>
395 struct result_to_tuple_container<result<E, Args...>> {
396 using type = tuple_container_t<Args...>;
397 };
398
399 template <class E, class T>
401 using type = T;
402 };
403
404 template <class E>
408
409 template <class E, std::size_t... Is, class Tuple>
410 [[nodiscard]] auto result_from_tuple(std::index_sequence<Is...>, Tuple &&tpl) {
411 using result_t = typename tuple_to_result<E, std::decay_t<Tuple>>::type;
412 if constexpr (result_t::value_size == 0) {
413 return result_t{result_success};
414 } else {
415 using tuple_t = typename result_to_tuple_container<result_t>::type;
416 // Explicitly construct the tuple
417 return result_t{tuple_t{std::get<Is>(tpl)...}};
418 }
419 }
420
421 static_assert(std::is_same_v<result<char>, decltype(result_from_tuple<char>(std::make_index_sequence<0>{}, std::tuple<>{}))>);
422 static_assert(std::is_same_v<result<char, int>, decltype(result_from_tuple<char>(std::make_index_sequence<1>{}, std::tuple<int>{}))>);
423
424 }// namespace impl
425
426 template <is_result R>
427 auto result_to_tuple(R &&r) {
428 return impl::result_to_tuple(std::make_index_sequence<std::decay_t<R>::value_size>{}, std::forward<R>(r));
429 }
430
431 template <class E, class Tuple>
432 auto result_from_tuple(Tuple &&tpl) {
433 return impl::result_from_tuple<E>(std::make_index_sequence<std::tuple_size_v<Tuple>>{}, tpl);
434 }
435
436 static_assert(std::is_same_v<result<char>, decltype(result_from_tuple<char>(std::tuple<>{}))>);
437 static_assert(std::is_same_v<result<char, int>, decltype(result_from_tuple<char>(std::tuple<int>{}))>);
438
439 template <is_result R1, is_result... Rs>
440 decltype(auto) concat_result(R1 &&r1, Rs &&...rs) {
441 if constexpr (sizeof...(Rs) == 0) {
442 return std::forward<R1>(r1);
443 } else {
444 static_assert(std::conjunction_v<std::is_same<typename std::decay_t<R1>::error_type, typename std::decay_t<Rs>::error_type>...>,
445 "All result types to concatenate must have the same error type.");
446 using error_t = typename std::decay_t<R1>::error_type;
447 using result_t = decltype(result_from_tuple<error_t>(
448 std::tuple_cat(result_to_tuple(std::forward<R1>(r1)),
449 result_to_tuple(std::forward<Rs>(rs))...)));
450
451 auto all_viable_recursive = [](auto &self, auto const &refr1, auto const &...refrs) -> result<error_t> {
452 if (not refr1) {
453 return refr1.error();
454 } else {
455 if constexpr (sizeof...(refrs) == 0) {
457 } else {
458 return self(self, refrs...);
459 }
460 }
461 };
462
463 if (const auto all_viable = all_viable_recursive(all_viable_recursive, r1, rs...); not all_viable) {
464 return result_t{all_viable.error()};
465 }
466
468 std::tuple_cat(result_to_tuple(std::forward<R1>(r1)),
469 result_to_tuple(std::forward<Rs>(rs))...));
470 }
471 }
472
473 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char>>(), std::declval<result<char>>())), result<char>>);
474 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int>>(), std::declval<result<char>>())), result<char, int>>);
475 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char>>(), std::declval<result<char, int>>())), result<char, int>>);
476 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int>>(), std::declval<result<char, float>>())), result<char, int, float>>);
477 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int, float>>(), std::declval<result<char, double>>())), result<char, int, float, double>>);
478 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int, float>>(), std::declval<result<char, double, unsigned>>())), result<char, int, float, double, unsigned>>);
479 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int, float, char>>(), std::declval<result<char, double, unsigned>>())), result<char, int, float, char, double, unsigned>>);
480 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int, float, char>>(), std::declval<result<char, double, unsigned, char>>())), result<char, int, float, char, double, unsigned, char>>);
481 static_assert(std::is_same_v<decltype(concat_result(std::declval<result<char, int, float, char>>(), std::declval<result<char, double>>(), std::declval<result<char, unsigned, char>>())), result<char, int, float, char, double, unsigned, char>>);
482
483}// namespace mlab
484
485#endif//MLAB_RESULT_HPP
Definition any_of.hpp:17
result & operator=(E error)
Definition result.hpp:272
typename result_impl< E, T >::template content_wrap< RC > content_wrap
Definition result.hpp:81
std::tuple< T > value_type_as_tuple
Definition result.hpp:92
E error_type
Definition result.hpp:89
result(result &&) noexcept=default
T value_type
Definition result.hpp:90
result(result &&) noexcept=default
std::tuple< Ts... > value_type_as_tuple
Definition result.hpp:158
Definition result.hpp:180
result(result &&) noexcept=default
std::tuple<> value_type_as_tuple
Definition result.hpp:186
result(result &&) noexcept=default
Definition result.hpp:30
Definition result.hpp:40
auto result_from_tuple(std::index_sequence< Is... >, Tuple &&tpl)
Definition result.hpp:410
auto result_to_tuple(std::index_sequence< Is... >, R &&r)
Definition result.hpp:379
Definition log.cpp:8
auto result_to_tuple(R &&r)
Definition result.hpp:427
static constexpr result_success_type result_success
Definition result.hpp:63
decltype(auto) get(R &&r)
Definition result.hpp:243
decltype(auto) concat_result(R1 &&r1, Rs &&...rs)
Concatenates as many results as provided. The returned object will contain all result::value_type of ...
Definition result.hpp:440
auto result_from_tuple(Tuple &&tpl)
Definition result.hpp:432
typename impl::tuple_container_type< Ts... >::type tuple_container_t
Definition result.hpp:148
result_content
Definition result.hpp:15
tuple_container_t< Args... > type
Definition result.hpp:396
Definition result.hpp:392
std::pair< T1, T2 > type
Definition result.hpp:137
Definition result.hpp:131
std::tuple< Args... > type
Definition result.hpp:132
Definition result.hpp:384
Definition result.hpp:72
content_type content
Definition result.hpp:74
typename std::conditional< RC==result_content::error, E, T >::type content_type
Definition result.hpp:73
Definition result.hpp:70
Definition result.hpp:55
bool operator==(result< E, T > const &res) const
Definition result.hpp:356
bool operator!=(result< E, T > const &res) const
Definition result.hpp:359