FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1#pragma once
2// Minimal expected<T, E>: the hot path never throws, it returns Result.
3//
4// Result<ClientOrderId, RejectReason> r = ctx.send(order);
5// if (!r) return r.error();
6// use(r.value());
7//
8// Errors are constructed via fastmm::fail(e) so that `Result<int, int>` is unambiguous.
9#include "fastmm/core/config_macros.hpp"
10
11#include <new>
12#include <type_traits>
13#include <utility>
14
15namespace fastmm {
16
17template <class E>
18struct Unexpected {
19 E error;
20};
22template <class E>
23[[nodiscard]] constexpr Unexpected<std::decay_t<E>> fail(E&& e) noexcept {
24 return Unexpected<std::decay_t<E>>{std::forward<E>(e)};
26
27template <class T, class E>
28class [[nodiscard]] Result {
29 static_assert(!std::is_void_v<E>, "error type must not be void");
30 static_assert(!std::is_reference_v<T> && !std::is_reference_v<E>);
31
32 public:
33 using value_type = T;
34 using error_type = E;
36 // NOLINTNEXTLINE(google-explicit-constructor): value → Result is the whole point.
37 constexpr Result(const T& v) noexcept(std::is_nothrow_copy_constructible_v<T>)
38 : has_value_(true) {
39 ::new (static_cast<void*>(&value_)) T(v);
40 }
41 // NOLINTNEXTLINE(google-explicit-constructor)
42 constexpr Result(T&& v) noexcept(std::is_nothrow_move_constructible_v<T>) : has_value_(true) {
43 ::new (static_cast<void*>(&value_)) T(std::move(v));
44 }
45 // NOLINTNEXTLINE(google-explicit-constructor)
46 constexpr Result(Unexpected<E> u) noexcept(std::is_nothrow_move_constructible_v<E>)
47 : has_value_(false) {
48 ::new (static_cast<void*>(&error_)) E(std::move(u.error));
49 }
51 constexpr Result(const Result& o) noexcept(std::is_nothrow_copy_constructible_v<T> &&
52 std::is_nothrow_copy_constructible_v<E>)
53 : has_value_(o.has_value_) {
54 if (has_value_) {
55 ::new (static_cast<void*>(&value_)) T(o.value_);
56 } else {
57 ::new (static_cast<void*>(&error_)) E(o.error_);
58 }
59 }
60 constexpr Result(Result&& o) noexcept(std::is_nothrow_move_constructible_v<T> &&
61 std::is_nothrow_move_constructible_v<E>)
62 : has_value_(o.has_value_) {
63 if (has_value_) {
64 ::new (static_cast<void*>(&value_)) T(std::move(o.value_));
65 } else {
66 ::new (static_cast<void*>(&error_)) E(std::move(o.error_));
67 }
68 }
69 constexpr Result& operator=(const Result& o) {
70 if (this != &o) {
71 destroy();
72 has_value_ = o.has_value_;
73 if (has_value_) {
74 ::new (static_cast<void*>(&value_)) T(o.value_);
75 } else {
76 ::new (static_cast<void*>(&error_)) E(o.error_);
77 }
78 }
79 return *this;
80 }
81 constexpr Result& operator=(Result&& o) noexcept(std::is_nothrow_move_constructible_v<T> &&
82 std::is_nothrow_move_constructible_v<E>) {
83 if (this != &o) {
84 destroy();
85 has_value_ = o.has_value_;
86 if (has_value_) {
87 ::new (static_cast<void*>(&value_)) T(std::move(o.value_));
88 } else {
89 ::new (static_cast<void*>(&error_)) E(std::move(o.error_));
90 }
91 }
92 return *this;
93 }
94 ~Result() { destroy(); }
96 [[nodiscard]] constexpr bool has_value() const noexcept { return has_value_; }
97 constexpr explicit operator bool() const noexcept { return has_value_; }
99 [[nodiscard]] constexpr T& value() & noexcept {
100 FASTMM_ASSERT(has_value_);
101 return value_;
103 [[nodiscard]] constexpr const T& value() const& noexcept {
104 FASTMM_ASSERT(has_value_);
105 return value_;
107 [[nodiscard]] constexpr T&& value() && noexcept {
108 FASTMM_ASSERT(has_value_);
109 return std::move(value_);
111 [[nodiscard]] constexpr const E& error() const noexcept {
112 FASTMM_ASSERT(!has_value_);
113 return error_;
115 [[nodiscard]] constexpr E& error() noexcept {
116 FASTMM_ASSERT(!has_value_);
117 return error_;
119 constexpr T& operator*() noexcept { return value(); }
120 constexpr const T& operator*() const noexcept { return value(); }
121 constexpr T* operator->() noexcept { return &value(); }
122 constexpr const T* operator->() const noexcept { return &value(); }
123
124 template <class U>
125 [[nodiscard]] constexpr T value_or(U&& fallback) const {
126 return has_value_ ? value_ : static_cast<T>(std::forward<U>(fallback));
127 }
128
129 // Monadic chaining: f(T) must return Result<U, E>.
130 template <class F>
131 [[nodiscard]] constexpr auto and_then(F&& f) const -> std::invoke_result_t<F, const T&> {
132 using R = std::invoke_result_t<F, const T&>;
133 static_assert(std::is_same_v<typename R::error_type, E>);
134 if (has_value_) return std::forward<F>(f)(value_);
135 return R(fail(error_));
136 }
137 // map: f(T) returns U, yields Result<U, E>.
138 template <class F>
139 [[nodiscard]] constexpr auto map(F&& f) const -> Result<std::invoke_result_t<F, const T&>, E> {
140 using U = std::invoke_result_t<F, const T&>;
141 if (has_value_) return Result<U, E>(std::forward<F>(f)(value_));
142 return Result<U, E>(fail(error_));
143 }
144
145 private:
146 constexpr void destroy() noexcept {
147 if constexpr (!std::is_trivially_destructible_v<T> || !std::is_trivially_destructible_v<E>) {
148 if (has_value_) {
149 value_.~T();
150 } else {
151 error_.~E();
152 }
153 }
154 }
155
156 union {
157 T value_;
158 E error_;
159 };
160 bool has_value_;
161};
162
163// Result<void, E>: success carries nothing.
164template <class E>
165class [[nodiscard]] Result<void, E> {
166 public:
167 using value_type = void;
168 using error_type = E;
170 constexpr Result() noexcept : error_(), has_value_(true) {}
171 // NOLINTNEXTLINE(google-explicit-constructor)
172 constexpr Result(Unexpected<E> u) noexcept : error_(std::move(u.error)), has_value_(false) {}
174 [[nodiscard]] constexpr bool has_value() const noexcept { return has_value_; }
175 constexpr explicit operator bool() const noexcept { return has_value_; }
176 [[nodiscard]] constexpr const E& error() const noexcept {
177 FASTMM_ASSERT(!has_value_);
178 return error_;
180 template <class F>
181 [[nodiscard]] constexpr auto and_then(F&& f) const -> std::invoke_result_t<F> {
182 using R = std::invoke_result_t<F>;
183 if (has_value_) return std::forward<F>(f)();
184 return R(fail(error_));
185 }
186
187 private:
188 E error_;
189 bool has_value_;
190};
191
192} // namespace fastmm
constexpr Result(Unexpected< E > u) noexcept
Definition result.hpp:170
E error_type
Definition result.hpp:167
void value_type
Definition result.hpp:166
Definition result.hpp:30
E error_type
Definition result.hpp:36
E error_
Definition result.hpp:157
T value_
Definition result.hpp:156
constexpr Result(T &&v) noexcept(std::is_nothrow_move_constructible_v< T >)
Definition result.hpp:42
constexpr const T * operator->() const noexcept
Definition result.hpp:121
T value_type
Definition result.hpp:35
constexpr const T & operator*() const noexcept
Definition result.hpp:119
Definition strategy.hpp:16
constexpr Fixed< T > operator*(Fixed< T > v, Ratio r) noexcept
value * ratio in the value's domain, through Int128 with one truncation toward zero: mid * 5_bps,...
Definition fixed_point.hpp:343
constexpr Unexpected< std::decay_t< E > > fail(E &&e) noexcept
Definition result.hpp:25
Definition result.hpp:20
E error
Definition result.hpp:21