载入中...
搜索中...
未找到
Result.h
浏览该文件的文档.
1#pragma once
2
8#include "common/Assert.h"
9#include "common/Status.h"
10
11#include <exception>
12#include <functional>
13#include <optional>
14#include <string>
15#include <string_view>
16#include <type_traits>
17#include <utility>
18#include <vector>
19
20namespace eve {
21namespace detail {
22
33public:
35 ResultObservation() noexcept = default;
36
38 ResultObservation& operator=(const ResultObservation&) = delete;
39
41 struct InactiveTag {};
42 explicit ResultObservation(InactiveTag) noexcept {
43 mustObserve_ = false;
44 }
45
46#if !defined(ZEROERR_NO_ASSERT)
47 ~ResultObservation() noexcept(false) {
48 const bool observationSatisfied = !mustObserve_ || observed_;
49 EV_ASSERT(observationSatisfied, "Result destroyed without checking; inspect it or call ignore() explicitly");
50 }
51#else
52 ~ResultObservation() noexcept = default;
53#endif
54
55protected:
57 void observe() const noexcept {
58#if !defined(ZEROERR_NO_ASSERT)
59 observed_ = true;
60#endif
61 }
62
65#if !defined(ZEROERR_NO_ASSERT)
66 const bool observationSatisfied = !mustObserve_ || observed_;
68 EV_ASSERT(observationSatisfied, "move assignment would overwrite an unchecked Result");
69#endif
70 }
71
74#if !defined(ZEROERR_NO_ASSERT)
75 observed_ = other.observed_;
76 mustObserve_ = other.mustObserve_;
77 other.mustObserve_ = false;
78#else
79 (void)other;
80#endif
81 }
82
84 void disarmObservation() noexcept {
85#if !defined(ZEROERR_NO_ASSERT)
86 mustObserve_ = false;
87#endif
88 }
89
91 void requireObservation() noexcept {
92#if !defined(ZEROERR_NO_ASSERT)
93 observed_ = false;
94 mustObserve_ = true;
95#endif
96 }
97
98private:
99 // Do not condition these fields on ZEROERR_NO_ASSERT. Public Result<T>
100 // values cross static/shared-library boundaries whose assertion policy may
101 // intentionally differ from the consumer (notably Release unit tests).
102 mutable bool observed_ = false;
103 bool mustObserve_ = true;
104};
105
106template <class T>
107using ResultReturn = std::invoke_result_t<T>;
108
116template <class From, class To>
118 std::is_same_v<To, const From> ||
119 (std::is_pointer_v<From> && std::is_same_v<To, const std::remove_pointer_t<From>*>);
120
127template <class T, class U>
129void moveAssignOptional(std::optional<T>& destination, std::optional<U>&& source) {
130 destination.reset();
131 if (source.has_value()) destination.emplace(std::move(*source));
132}
133
134} // namespace detail
135
153template <class T>
155class [[nodiscard("Result must be checked or explicitly ignored")]] Result : private detail::ResultObservation {
156 static_assert(!std::is_reference_v<T>, "Result<T> cannot hold a reference; use a handle or value");
157 static_assert(!std::is_void_v<T>, "Result<void> has a dedicated specialization");
158
159 template <class U>
160 friend class Result;
161
162public:
164 static Result success(T value) { return Result(Status::success(), std::optional<T>(std::move(value))); }
165
169 EV_ASSERT(status.isSuccess(), "Result::success requires a successful Status");
171 return Result(std::move(status), std::optional<T>(std::move(value)));
172 }
173
177 EV_ASSERT(status.isFailure(), "Result::failure requires a failure Status");
179 return Result(std::move(status), std::nullopt);
180 }
181
183 static Result failure(Diagnostic diagnostic) { return failure(Status::failure(std::move(diagnostic))); }
184
185 Result(const Result&) = delete;
186 Result& operator=(const Result&) = delete;
187
189 Result(Result&& other)
191 : detail::ResultObservation(detail::ResultObservation::InactiveTag{}),
193 status_(std::move(other.status_)),
195 value_(std::move(other.value_)) {
197 adoptObservationFrom(other);
198 }
199
206 if (this == &other) return *this;
208 assertCanBeOverwritten();
209 status_ = std::move(other.status_);
211 detail::moveAssignOptional(value_, std::move(other.value_));
213 adoptObservationFrom(other);
214 return *this;
215 }
216
222 template <class U>
227 : detail::ResultObservation(detail::ResultObservation::InactiveTag{}),
229 status_(std::move(other.status_)),
231 value_(std::optional<T>(std::move(other.value_))) {
233 adoptObservationFrom(other);
234 }
235
240 template <class U>
245 assertCanBeOverwritten();
246 status_ = std::move(other.status_);
248 detail::moveAssignOptional(value_, std::move(other.value_));
250 adoptObservationFrom(other);
251 return *this;
252 }
253
255 bool ok() const noexcept {
257 observe();
258 return status_.isSuccess() && value_.has_value();
259 }
260
262 bool hasValue() const noexcept {
264 observe();
265 return status_.isSuccess() && value_.has_value();
266 }
267
269 const Status& status() const noexcept {
271 observe();
272 return status_;
273 }
274
276 StatusCode code() const noexcept {
278 observe();
279 return status_.code();
280 }
281
283 const std::vector<Diagnostic>& diagnostics() const noexcept {
285 observe();
286 return status_.diagnostics();
287 }
288
298 const Diagnostic* error() const noexcept {
300 observe();
301 return status_.primaryDiagnostic();
302 }
303
305 explicit operator bool() const noexcept { return ok(); }
306
308 const T& value() const& {
310 observe();
311 const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value();
313 EV_ASSERT(hasSuccessfulValue, "Result::value requires a successful Result with a value");
314 return value_.value();
315 }
316
318 T& value() & {
320 observe();
321 const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value();
323 EV_ASSERT(hasSuccessfulValue, "Result::value requires a successful Result with a value");
324 return value_.value();
325 }
326
328 T&& value() && {
330 observe();
331 const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value();
333 EV_ASSERT(hasSuccessfulValue, "Result::value requires a successful Result with a value");
335 return std::move(value_.value());
336 }
337
339 T takeValue() && {
341 observe();
342 const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value();
344 EV_ASSERT(hasSuccessfulValue, "Result::takeValue requires a successful Result with a value");
345 T result = std::move(value_.value());
346 value_.reset();
347 return result;
348 }
349
351 T valueOr(T fallback) && {
353 observe();
354 if (status_.isSuccess() && value_.has_value()) return std::move(value_.value());
355 return fallback;
356 }
357
363 template <class Function>
365 auto andThen(Function&& function) && -> std::invoke_result_t<Function, T&&> {
367 observe();
368 using Return = std::invoke_result_t<Function, T&&>;
369 if (!status_.isSuccess() || !value_.has_value()) return Return::failure(status_);
371 return std::invoke(std::forward<Function>(function), std::move(value_.value()));
372 }
373
379 template <class Function>
381 Result orElse(Function&& function) && {
383 observe();
384 if (status_.isSuccess() && value_.has_value()) return std::move(*this);
386 return std::invoke(std::forward<Function>(function), status_);
387 }
388
393 void ignore(std::string_view reason = {}) const noexcept {
394 (void)reason;
396 observe();
397 }
398
405 T expect(std::string_view message) && {
407 observe();
408 if (!status_.isSuccess() || !value_.has_value()) {
410 const std::string context(message);
411 const std::string detail = status_.describe();
413 EV_ASSERT(false, "%s: %s", context.c_str(), detail.c_str());
415 std::terminate();
416 }
418 return std::move(value_.value());
419 }
420
421private:
422 Result(Status status, std::optional<T> value) : status_(std::move(status)), value_(std::move(value)) {}
423
424 Status status_;
425 std::optional<T> value_;
426};
427
431template <>
433class [[nodiscard("Result must be checked or explicitly ignored")]] Result<void> : private detail::ResultObservation {
434public:
436 static Result success() { return Result(Status::success()); }
437
441 EV_ASSERT(status.isSuccess(), "Result::success requires a successful Status");
443 return Result(std::move(status));
444 }
445
449 EV_ASSERT(status.isFailure(), "Result::failure requires a failure Status");
451 return Result(std::move(status));
452 }
453
455 static Result failure(Diagnostic diagnostic) { return failure(Status::failure(std::move(diagnostic))); }
456
457 Result(const Result&) = delete;
458 Result& operator=(const Result&) = delete;
459
461 Result(Result&& other)
463 : detail::ResultObservation(detail::ResultObservation::InactiveTag{}), status_(std::move(other.status_)) {
465 adoptObservationFrom(other);
466 }
467
470 if (this == &other) return *this;
472 assertCanBeOverwritten();
473 status_ = std::move(other.status_);
475 adoptObservationFrom(other);
476 return *this;
477 }
478
480 bool ok() const noexcept {
482 observe();
483 return status_.isSuccess();
484 }
485
487 const Status& status() const noexcept {
489 observe();
490 return status_;
491 }
492
494 StatusCode code() const noexcept {
496 observe();
497 return status_.code();
498 }
499
501 const std::vector<Diagnostic>& diagnostics() const noexcept {
503 observe();
504 return status_.diagnostics();
505 }
506
516 const Diagnostic* error() const noexcept {
518 observe();
519 return status_.primaryDiagnostic();
520 }
521
523 explicit operator bool() const noexcept { return ok(); }
524
526 void value() const {
528 observe();
530 EV_ASSERT(status_.isSuccess(), "Result<void>::value requires a successful Result");
531 }
532
537 void ignore(std::string_view reason = {}) const noexcept {
538 (void)reason;
540 observe();
541 }
542
548 void expect(std::string_view message) const {
550 observe();
551 if (!status_.isSuccess()) {
553 const std::string context(message);
554 const std::string detail = status_.describe();
556 EV_ASSERT(false, "%s: %s", context.c_str(), detail.c_str());
558 std::terminate();
559 }
560 }
561
562private:
563 explicit Result(Status status) : status_(std::move(status)) {}
564
565 Status status_;
566};
567
584template <typename... Results>
586[[nodiscard]] bool everyResultValid(const Results&... results) noexcept {
587 static_assert(sizeof...(Results) > 0, "everyResultValid requires at least one Result");
588 return (results.ok() & ...);
589}
590
591} // namespace eve
double value
EVEngine assertion entry point, backed by zeroerr.
#define EV_ASSERT(cond,...)
Assert an internal engine invariant (state that must always hold).
Definition Assert.h:37
std::string message
wgpu::PopErrorScopeStatus status
int detail
MigrationFunction function
Structured operation status used by the common Result foundation.
const UnitySourceAsset & source
const VegetationPresetContext & context
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
const Diagnostic * error() const noexcept
Inspect the first diagnostic, or null when none was supplied.
Definition Result.h:516
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
Result & operator=(const Result &)=delete
Result(Result &&other)
Move a result and transfer its debug observation responsibility.
Definition Result.h:461
static Result failure(Status status)
Construct a failed void result from a structured status.
Definition Result.h:447
void expect(std::string_view message) const
Require success for a void operation.
Definition Result.h:548
void value() const
Mark a successful void operation as checked.
Definition Result.h:526
static Result success(Status status)
Construct a successful void result with an explicit status.
Definition Result.h:439
static Result failure(Diagnostic diagnostic)
Construct a failed void result from one structured diagnostic.
Definition Result.h:455
static Result success()
Construct a successful void result.
Definition Result.h:436
const std::vector< Diagnostic > & diagnostics() const noexcept
Inspect all diagnostics; this counts as checking the Result.
Definition Result.h:501
Result & operator=(Result &&other)
Move-assign after checking the destination's old result.
Definition Result.h:469
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
Result(const Result &)=delete
StatusCode code() const noexcept
Inspect the stable operation code.
Definition Result.h:494
const Status & status() const noexcept
Inspect the structured operation status.
Definition Result.h:487
Move-only operation result carrying either a value or Status.
Definition Result.h:155
const Diagnostic * error() const noexcept
Inspect the first diagnostic, or null when none was supplied.
Definition Result.h:298
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
Result orElse(Function &&function) &&
Recover a failure with a function receiving its Status.
Definition Result.h:381
T expect(std::string_view message) &&
Require success and move out the value.
Definition Result.h:405
T takeValue() &&
Move the value out and remove it from this Result.
Definition Result.h:339
T valueOr(T fallback) &&
Return the value or an explicit alternate value, consuming this Result.
Definition Result.h:351
const T & value() const &
Borrow the value from a const lvalue after checking success.
Definition Result.h:308
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:393
Result & operator=(const Result &)=delete
T && value() &&
Move the value out after checking success.
Definition Result.h:328
T & value() &
Borrow the value from a mutable lvalue after checking success.
Definition Result.h:318
Result & operator=(Result< U > &&other)
Move-assign from a Result whose value converts to T by adding const.
Definition Result.h:243
Result(Result< U > &&other)
Convert a Result into a more const-qualified Result of the same outcome.
Definition Result.h:225
auto andThen(Function &&function) &&-> std::invoke_result_t< Function, T && >
Compose a successful value with a function returning another Result.
Definition Result.h:365
bool hasValue() const noexcept
Whether the result owns a value; observing this is a check.
Definition Result.h:262
Result & operator=(Result &&other)
Move-assign a result after checking the destination's old result.
Definition Result.h:205
const std::vector< Diagnostic > & diagnostics() const noexcept
Inspect all diagnostics; this counts as checking the Result.
Definition Result.h:283
Result(Result &&other)
Move a result and transfer its debug observation responsibility.
Definition Result.h:189
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
StatusCode code() const noexcept
Inspect the stable operation code.
Definition Result.h:276
Result(const Result &)=delete
static Result success(T value, Status status)
Construct a successful result with an explicit non-error status.
Definition Result.h:167
const Status & status() const noexcept
Inspect the structured operation status.
Definition Result.h:269
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Result failure(Diagnostic diagnostic)
Construct a failed result from one structured diagnostic.
Definition Result.h:183
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
Debug-only observation token shared by Result<T> specializations.
Definition Result.h:32
void assertCanBeOverwritten() const
Assert can be overwritten.
Definition Result.h:64
void disarmObservation() noexcept
Drop observation duty; used when a moved-from result is empty.
Definition Result.h:84
~ResultObservation() noexcept(false)
Definition Result.h:47
void observe() const noexcept
Observe.
Definition Result.h:57
ResultObservation() noexcept=default
Constructs a ResultObservation.
void adoptObservationFrom(ResultObservation &other) noexcept
Adopt observation from.
Definition Result.h:73
ResultObservation(InactiveTag) noexcept
Definition Result.h:42
void requireObservation() noexcept
Require a fresh check after replacing the carried outcome.
Definition Result.h:91
True when From may convert into To only by adding const.
Definition Result.h:117
void moveAssignOptional(std::optional< T > &destination, std::optional< U > &&source)
Replace destination with source even when T is not assignable.
Definition Result.h:129
std::invoke_result_t< T > ResultReturn
Definition Result.h:107
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
StatusCode
Stable outcome category for an operation.
Definition Status.h:27
bool everyResultValid(const Results &... results) noexcept
True only when every argument Result succeeded, observing all of them.
Definition Result.h:586
Start an inactive token used while a Result move is constructed.
Definition Result.h:41