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
28template <typename T, typename E = FormattableError>
30 public:
39 // NOLINTNEXTLINE(google-explicit-constructor)
40 ChainableResult(T value) : result_{std::move(value)} {}
41
50 // NOLINTNEXTLINE(google-explicit-constructor)
51 ChainableResult(const E &error) : result_{std::unexpected{error}}
52 {
53 static_assert(std::is_base_of_v<Error, E>, "ChainableResult error type E must be derived from Error");
54 error_chain_.push_back(std::make_unique<E>(error));
55 }
56
69 template <typename CauseT, typename CauseE>
70 explicit ChainableResult(const E &error, const ChainableResult<CauseT, CauseE> &cause_result)
71 : result_{std::unexpected{error}}
72 {
73 static_assert(std::is_base_of_v<Error, E>, "ChainableResult error type E must be derived from Error");
74 error_chain_.push_back(std::make_unique<E>(result_.error()));
75 add_cause(cause_result);
76 }
77
89 template <typename CauseT, typename CauseE>
90 explicit ChainableResult(const ChainableResult<CauseT, CauseE> &cause_result) : result_{std::unexpected{E{}}}
91 {
92 static_assert(std::is_base_of_v<Error, E>, "ChainableResult error type E must be derived from Error");
93 error_chain_.push_back(std::make_unique<E>(result_.error()));
94 add_cause(cause_result);
95 }
96
97 // Move-only semantics
102
113 template <typename OtherT, typename OtherE>
115 {
116 assert_or_panic(!cause_result.has_value(), "cause_result has a value, but should have an error");
117
118 // Clone errors from cause_result's chain since we can't move from const
119 for (const std::unique_ptr<Error> &err : cause_result.chain()) {
120 error_chain_.push_back(err->clone());
121 }
122 }
123
127 [[nodiscard]] bool has_value() const
128 {
129 return result_.has_value();
130 }
131
139 [[nodiscard]] T &value() &
140 {
141 return result_.value();
142 }
143
151 [[nodiscard]] const T &value() const &
152 {
153 return result_.value();
154 }
155
164 [[nodiscard]] T &&value() &&
165 {
166 return std::move(result_).value();
167 }
168
176 [[nodiscard]] const E &error() const
177 {
178 return result_.error();
179 }
180
189 [[nodiscard]] const std::vector<std::unique_ptr<Error>> &chain() const
190 {
191 return error_chain_;
192 }
193
194 protected:
195 // Protected default constructor for use by the void specialization
196 ChainableResult() = default;
197
198 private:
199 std::expected<T, E> result_;
200 std::vector<std::unique_ptr<Error>> error_chain_;
201};
202
203namespace detail {
204// An empty struct to use as a placeholder for `void` in the ChainableResult specialization.
205struct Empty {};
206} // namespace detail
207
217template <typename E>
218class ChainableResult<void, E> : public ChainableResult<detail::Empty, E> {
220
221 public:
228 ChainableResult() : Base{detail::Empty{}} {}
229
239
252 template <typename CauseT, typename CauseE>
253 explicit ChainableResult(const E &error, const ChainableResult<CauseT, CauseE> &cause_result)
254 : Base{error, cause_result}
255 {
256 }
257
268 template <typename CauseT, typename CauseE>
269 explicit ChainableResult(const ChainableResult<CauseT, CauseE> &cause_result) : Base{cause_result}
270 {
271 }
272
279 void value() &
280 {
281 Base::value();
282 }
283
290 void value() const &
291 {
292 Base::value();
293 }
294
301 void value() &&
302 {
303 std::move(*this).Base::value();
304 }
305};
306
326#define PT_TRY_ASSIGN_CHAIN_ERR(var, expr, return_type, ...) \
327 auto var##_result = (expr); \
328 if (!var##_result.has_value()) { \
329 return ChainableResult<return_type>{FormattableError{__VA_ARGS__}, var##_result}; \
330 } \
331 auto var = std::move(var##_result).value();
332
349#define PT_TRY_ASSIGN_PASS_ERR(var, expr, return_type) \
350 auto var##_result = (expr); \
351 if (!var##_result.has_value()) { \
352 return ChainableResult<return_type>{FormattableError{}, var##_result}; \
353 } \
354 auto var = std::move(var##_result).value();
355
369#define PT_TRY_ASSIGN_PASS_SAME_ERR(var, expr) \
370 auto var##_result = (expr); \
371 if (!var##_result.has_value()) { \
372 return var##_result; \
373 } \
374 auto var = std::move(var##_result).value();
375
376// Internal implementation detail - do not use directly
377#define PT_DETAIL_TRY_CALL_CHAIN_ERR_EXPAND(expr, return_type, counter, ...) \
378 auto pt_try_call_result_##counter = (expr); \
379 if (!pt_try_call_result_##counter.has_value()) { \
380 return ChainableResult<return_type>{FormattableError{__VA_ARGS__}, pt_try_call_result_##counter}; \
381 }
382
383// Internal implementation detail - do not use directly
384#define PT_DETAIL_TRY_CALL_CHAIN_ERR_IMPL(expr, return_type, counter, ...) \
385 PT_DETAIL_TRY_CALL_CHAIN_ERR_EXPAND(expr, return_type, counter, __VA_ARGS__)
386
409#define PT_TRY_CALL_CHAIN_ERR(expr, return_type, ...) \
410 PT_DETAIL_TRY_CALL_CHAIN_ERR_IMPL(expr, return_type, __LINE__, __VA_ARGS__)
411
412// Internal implementation detail - do not use directly
413#define PT_DETAIL_TRY_CALL_PASS_ERR_EXPAND(expr, return_type, counter) \
414 auto pt_try_call_result_##counter = (expr); \
415 if (!pt_try_call_result_##counter.has_value()) { \
416 return ChainableResult<return_type>{FormattableError{}, pt_try_call_result_##counter}; \
417 }
418
419// Internal implementation detail - do not use directly
420#define PT_DETAIL_TRY_CALL_PASS_ERR_IMPL(expr, return_type, counter) \
421 PT_DETAIL_TRY_CALL_PASS_ERR_EXPAND(expr, return_type, counter)
422
442#define PT_TRY_CALL_PASS_ERR(expr, return_type) PT_DETAIL_TRY_CALL_PASS_ERR_IMPL(expr, return_type, __LINE__)
443
444// Internal implementation detail - do not use directly
445#define PT_DETAIL_TRY_CALL_PASS_SAME_ERR_EXPAND(expr, counter) \
446 auto pt_try_call_result_##counter = (expr); \
447 if (!pt_try_call_result_##counter.has_value()) { \
448 return pt_try_call_result_##counter; \
449 }
450
451// Internal implementation detail - do not use directly
452#define PT_DETAIL_TRY_CALL_PASS_SAME_ERR_IMPL(expr, counter) PT_DETAIL_TRY_CALL_PASS_SAME_ERR_EXPAND(expr, counter)
453
472#define PT_TRY_CALL_PASS_SAME_ERR(expr) PT_DETAIL_TRY_CALL_PASS_SAME_ERR_IMPL(expr, __LINE__)
473
474} // 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