Porytiles
Loading...
Searching...
No Matches
chainable_result.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <expected>
4#include <memory>
5#include <string>
6#include <type_traits>
7
10
11namespace porytiles {
12
30template <typename T, typename E = FormattableError>
32 public:
43 // NOLINTNEXTLINE(google-explicit-constructor)
44 ChainableResult(T value) : result_{std::move(value)} {}
45
56 // NOLINTNEXTLINE(google-explicit-constructor)
57 ChainableResult(const E &error) : result_{std::unexpected{error}}
58 {
59 static_assert(std::is_base_of_v<Error, E>, "ChainableResult error type E must be derived from Error");
60 error_chain_.push_back(std::make_unique<E>(error));
61 }
62
77 template <typename CauseT, typename CauseE>
78 explicit ChainableResult(const E &error, const ChainableResult<CauseT, CauseE> &cause_result)
79 : result_{std::unexpected{error}}
80 {
81 static_assert(std::is_base_of_v<Error, E>, "ChainableResult error type E must be derived from Error");
82 error_chain_.push_back(std::make_unique<E>(result_.error()));
83 add_cause(cause_result);
84 }
85
99 template <typename CauseT, typename CauseE>
100 explicit ChainableResult(const ChainableResult<CauseT, CauseE> &cause_result) : result_{std::unexpected{E{}}}
101 {
102 static_assert(std::is_base_of_v<Error, E>, "ChainableResult error type E must be derived from Error");
103 error_chain_.push_back(std::make_unique<E>(result_.error()));
104 add_cause(cause_result);
105 }
106
107 /*
108 * Move-only semantics
109 */
114
127 template <typename OtherT, typename OtherE>
129 {
130 assert_or_panic(!cause_result.has_value(), "cause_result has a value, but should have an error");
131
132 // Clone errors from cause_result's chain since we can't move from const
133 for (const std::unique_ptr<Error> &err : cause_result.chain()) {
134 error_chain_.push_back(err->clone());
135 }
136 }
137
143 [[nodiscard]] bool has_value() const
144 {
145 return result_.has_value();
146 }
147
157 [[nodiscard]] T &value() &
158 {
159 return result_.value();
160 }
161
171 [[nodiscard]] const T &value() const &
172 {
173 return result_.value();
174 }
175
186 [[nodiscard]] T &&value() &&
187 {
188 return std::move(result_).value();
189 }
190
200 [[nodiscard]] const E &error() const
201 {
202 return result_.error();
203 }
204
215 [[nodiscard]] const std::vector<std::unique_ptr<Error>> &chain() const
216 {
217 return error_chain_;
218 }
219
220 protected:
221 // Protected default constructor for use by the void specialization
222 ChainableResult() = default;
223
224 private:
225 std::expected<T, E> result_;
226 std::vector<std::unique_ptr<Error>> error_chain_;
227};
228
229namespace detail {
230// An empty struct to use as a placeholder for `void` in the ChainableResult specialization.
231struct Empty {};
232} // namespace detail
233
245template <typename E>
246class ChainableResult<void, E> : public ChainableResult<detail::Empty, E> {
248
249 public:
258 ChainableResult() : Base{detail::Empty{}} {}
259
271
286 template <typename CauseT, typename CauseE>
287 explicit ChainableResult(const E &error, const ChainableResult<CauseT, CauseE> &cause_result)
288 : Base{error, cause_result}
289 {
290 }
291
304 template <typename CauseT, typename CauseE>
305 explicit ChainableResult(const ChainableResult<CauseT, CauseE> &cause_result) : Base{cause_result}
306 {
307 }
308
317 void value() &
318 {
319 Base::value();
320 }
321
330 void value() const &
331 {
332 Base::value();
333 }
334
343 void value() &&
344 {
345 std::move(*this).Base::value();
346 }
347};
348
370#define PT_TRY_ASSIGN_CHAIN_ERR(var, expr, return_type, ...) \
371 auto var##_result = (expr); \
372 if (!var##_result.has_value()) { \
373 return ChainableResult<return_type>{FormattableError{__VA_ARGS__}, var##_result}; \
374 } \
375 auto var = std::move(var##_result).value();
376
395#define PT_TRY_ASSIGN_PASS_ERR(var, expr, return_type) \
396 auto var##_result = (expr); \
397 if (!var##_result.has_value()) { \
398 return ChainableResult<return_type>{FormattableError{}, var##_result}; \
399 } \
400 auto var = std::move(var##_result).value();
401
417#define PT_TRY_ASSIGN_PASS_SAME_ERR(var, expr) \
418 auto var##_result = (expr); \
419 if (!var##_result.has_value()) { \
420 return var##_result; \
421 } \
422 auto var = std::move(var##_result).value();
423
424// Internal implementation detail - do not use directly
425#define PT_DETAIL_TRY_CALL_CHAIN_ERR_EXPAND(expr, return_type, counter, ...) \
426 auto pt_try_call_result_##counter = (expr); \
427 if (!pt_try_call_result_##counter.has_value()) { \
428 return ChainableResult<return_type>{FormattableError{__VA_ARGS__}, pt_try_call_result_##counter}; \
429 }
430
431// Internal implementation detail - do not use directly
432#define PT_DETAIL_TRY_CALL_CHAIN_ERR_IMPL(expr, return_type, counter, ...) \
433 PT_DETAIL_TRY_CALL_CHAIN_ERR_EXPAND(expr, return_type, counter, __VA_ARGS__)
434
459#define PT_TRY_CALL_CHAIN_ERR(expr, return_type, ...) \
460 PT_DETAIL_TRY_CALL_CHAIN_ERR_IMPL(expr, return_type, __LINE__, __VA_ARGS__)
461
462// Internal implementation detail - do not use directly
463#define PT_DETAIL_TRY_CALL_PASS_ERR_EXPAND(expr, return_type, counter) \
464 auto pt_try_call_result_##counter = (expr); \
465 if (!pt_try_call_result_##counter.has_value()) { \
466 return ChainableResult<return_type>{FormattableError{}, pt_try_call_result_##counter}; \
467 }
468
469// Internal implementation detail - do not use directly
470#define PT_DETAIL_TRY_CALL_PASS_ERR_IMPL(expr, return_type, counter) \
471 PT_DETAIL_TRY_CALL_PASS_ERR_EXPAND(expr, return_type, counter)
472
494#define PT_TRY_CALL_PASS_ERR(expr, return_type) PT_DETAIL_TRY_CALL_PASS_ERR_IMPL(expr, return_type, __LINE__)
495
496// Internal implementation detail - do not use directly
497#define PT_DETAIL_TRY_CALL_PASS_SAME_ERR_EXPAND(expr, counter) \
498 auto pt_try_call_result_##counter = (expr); \
499 if (!pt_try_call_result_##counter.has_value()) { \
500 return pt_try_call_result_##counter; \
501 }
502
503// Internal implementation detail - do not use directly
504#define PT_DETAIL_TRY_CALL_PASS_SAME_ERR_IMPL(expr, counter) PT_DETAIL_TRY_CALL_PASS_SAME_ERR_EXPAND(expr, counter)
505
526#define PT_TRY_CALL_PASS_SAME_ERR(expr) PT_DETAIL_TRY_CALL_PASS_SAME_ERR_IMPL(expr, __LINE__)
527
528} // namespace porytiles
ChainableResult(const ChainableResult< CauseT, CauseE > &cause_result)
Constructs a ChainableResult by chaining an existing error chain with a default-constructed error.
void value() &&
Accesses the void success value (rvalue version).
ChainableResult()
Default constructor creating a successful void result.
void value() &
Accesses the void success value.
ChainableResult(const E &error, const ChainableResult< CauseT, CauseE > &cause_result)
Constructs a ChainableResult by chaining a new error with an existing error chain.
void value() const &
Accesses the void success value (const version).
ChainableResult(const E &error)
Constructs a ChainableResult from an error value.
A result type that maintains a chainable sequence of errors for debugging and error reporting.
ChainableResult(ChainableResult &&)=default
ChainableResult & operator=(const ChainableResult &)=delete
const std::vector< std::unique_ptr< Error > > & chain() const
Returns the complete error chain.
ChainableResult & operator=(ChainableResult &&)=default
const T & value() const &
Returns a const reference to the contained success value.
ChainableResult(const ChainableResult< CauseT, CauseE > &cause_result)
Constructs a ChainableResult by chaining an existing error chain with a default-constructed error.
T & value() &
Returns a reference to the contained success value.
ChainableResult(const E &error)
Constructs a ChainableResult from an error value.
T && value() &&
Returns an rvalue reference to the contained success value.
ChainableResult(T value)
Constructs a ChainableResult from a success value.
bool has_value() const
Checks whether the result contains a success value.
ChainableResult(const ChainableResult &)=delete
void add_cause(const ChainableResult< OtherT, OtherE > &cause_result)
Adds all errors from another ChainableResult's chain to this result's chain.
const E & error() const
Returns a const reference to the immediate error.
ChainableResult(const E &error, const ChainableResult< CauseT, CauseE > &cause_result)
Constructs a ChainableResult by chaining a new error with an existing error chain.
void assert_or_panic(bool condition, const StringViewSourceLoc &s)
Conditionally panics if the given condition is false.
Definition panic.cpp:53