purify
C++ Purify implementation with native circuit and BPP support
Loading...
Searching...
No Matches
expected.hpp
Go to the documentation of this file.
1// Copyright (c) 2026 Judica, Inc.
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://opensource.org/license/mit/.
4
10#pragma once
11
12#include <exception>
13#include <memory>
14#include <new>
15#include <type_traits>
16#include <utility>
17
18namespace purify {
19
20// Public API types must keep one ABI across translation units, even when
21// consumers and the library are built with different C++ standard modes.
22struct unexpect_t {
23 explicit constexpr unexpect_t() noexcept = default;
24};
25
26inline constexpr unexpect_t unexpect{};
27
28template <typename E = void>
29class bad_expected_access : public std::exception {
30public:
31 const char* what() const noexcept override {
32 return "bad expected access";
33 }
34};
35
36template <typename E>
38public:
39 constexpr explicit Unexpected(const E& error) : error_(error) {}
40 constexpr explicit Unexpected(E&& error) : error_(std::move(error)) {}
41
42 [[nodiscard]] constexpr E& error() & noexcept {
43 return error_;
44 }
45
46 [[nodiscard]] constexpr const E& error() const& noexcept {
47 return error_;
48 }
49
50 [[nodiscard]] constexpr E&& error() && noexcept {
51 return std::move(error_);
52 }
53
54 [[nodiscard]] constexpr const E&& error() const&& noexcept {
55 return std::move(error_);
56 }
57
58private:
59 E error_;
60};
61
63template <typename T, typename E>
64class Expected {
65public:
66 using value_type = T;
67 using error_type = E;
69
71 requires std::is_default_constructible_v<T>
72 {
73 ConstructValue();
74 }
75
76 Expected(const T& value)
77 {
78 ConstructValue(value);
79 }
80 Expected(T&& value)
81 {
82 ConstructValue(std::move(value));
83 }
84 Expected(const Unexpected<E>& error)
85 {
86 ConstructError(error.error());
87 }
89 {
90 ConstructError(std::move(error).error());
91 }
92
93 Expected(const Expected& other)
94 {
95 if (other.has_value()) {
96 ConstructValue(other.ValueRef());
97 } else {
98 ConstructError(other.ErrorRef());
99 }
100 }
101
102 Expected(Expected&& other) noexcept(std::is_nothrow_move_constructible_v<T> &&
103 std::is_nothrow_move_constructible_v<E>)
104 {
105 if (other.has_value()) {
106 ConstructValue(std::move(other.ValueRef()));
107 } else {
108 ConstructError(std::move(other.ErrorRef()));
109 }
110 }
111
113 {
114 if (this == &other) {
115 return *this;
116 }
117 if (has_value() && other.has_value()) {
118 ValueRef() = other.ValueRef();
119 return *this;
120 }
121 if (!has_value() && !other.has_value()) {
122 ErrorRef() = other.ErrorRef();
123 return *this;
124 }
125 if (other.has_value()) {
126 T tmp(other.ValueRef());
127 Destroy();
128 ConstructValue(std::move(tmp));
129 return *this;
130 }
131 E tmp(other.ErrorRef());
132 Destroy();
133 ConstructError(std::move(tmp));
134 return *this;
135 }
136
137 Expected& operator=(Expected&& other) noexcept(std::is_nothrow_move_assignable_v<T> &&
138 std::is_nothrow_move_assignable_v<E> &&
139 std::is_nothrow_move_constructible_v<T> &&
140 std::is_nothrow_move_constructible_v<E>)
141 {
142 if (this == &other) {
143 return *this;
144 }
145 if (has_value() && other.has_value()) {
146 ValueRef() = std::move(other.ValueRef());
147 return *this;
148 }
149 if (!has_value() && !other.has_value()) {
150 ErrorRef() = std::move(other.ErrorRef());
151 return *this;
152 }
153 if (other.has_value()) {
154 T tmp(std::move(other.ValueRef()));
155 Destroy();
156 ConstructValue(std::move(tmp));
157 return *this;
158 }
159 E tmp(std::move(other.ErrorRef()));
160 Destroy();
161 ConstructError(std::move(tmp));
162 return *this;
163 }
164
166 {
167 Destroy();
168 }
169
170 [[nodiscard]] bool has_value() const noexcept {
171 return m_has_value;
172 }
173
174 [[nodiscard]] explicit operator bool() const noexcept {
175 return has_value();
176 }
177
178 [[nodiscard]] T& operator*() & {
179 return value();
180 }
181
182 [[nodiscard]] const T& operator*() const& {
183 return value();
184 }
185
186 [[nodiscard]] T&& operator*() && {
187 return std::move(value());
188 }
189
190 [[nodiscard]] const T&& operator*() const&& {
191 return std::move(value());
192 }
193
194 [[nodiscard]] T* operator->() {
195 return std::addressof(value());
196 }
197
198 [[nodiscard]] const T* operator->() const {
199 return std::addressof(value());
200 }
201
202 [[nodiscard]] T& value() & {
203 if (!has_value()) {
205 }
206 return ValueRef();
207 }
208
209 [[nodiscard]] const T& value() const& {
210 if (!has_value()) {
212 }
213 return ValueRef();
214 }
215
216 [[nodiscard]] T&& value() && {
217 if (!has_value()) {
219 }
220 return std::move(ValueRef());
221 }
222
223 [[nodiscard]] const T&& value() const&& {
224 if (!has_value()) {
226 }
227 return std::move(ValueRef());
228 }
229
230 [[nodiscard]] E& error() & {
231 if (has_value()) {
233 }
234 return ErrorRef();
235 }
236
237 [[nodiscard]] const E& error() const& {
238 if (has_value()) {
240 }
241 return ErrorRef();
242 }
243
244 [[nodiscard]] E&& error() && {
245 if (has_value()) {
247 }
248 return std::move(ErrorRef());
249 }
250
251 [[nodiscard]] const E&& error() const&& {
252 if (has_value()) {
254 }
255 return std::move(ErrorRef());
256 }
257
258private:
259 union Storage {
260 char empty;
261 T value;
262 E error;
263
264 Storage() noexcept : empty() {}
265 ~Storage() {}
266 };
267
268 template <typename... Args>
269 void ConstructValue(Args&&... args)
270 {
271 std::construct_at(std::addressof(storage_.value), std::forward<Args>(args)...);
272 m_has_value = true;
273 }
274
275 template <typename... Args>
276 void ConstructError(Args&&... args)
277 {
278 std::construct_at(std::addressof(storage_.error), std::forward<Args>(args)...);
279 m_has_value = false;
280 }
281
282 void Destroy() noexcept
283 {
284 if (m_has_value) {
285 std::destroy_at(std::addressof(storage_.value));
286 return;
287 }
288 std::destroy_at(std::addressof(storage_.error));
289 }
290
291 T& ValueRef() & noexcept
292 {
293 return storage_.value;
294 }
295
296 const T& ValueRef() const& noexcept
297 {
298 return storage_.value;
299 }
300
301 E& ErrorRef() & noexcept
302 {
303 return storage_.error;
304 }
305
306 const E& ErrorRef() const& noexcept
307 {
308 return storage_.error;
309 }
310
311 // Keep the fallback explicitly tagged; i686 was surfacing valueless
312 // std::variant states through Purify's checked-return path.
313 bool m_has_value{false};
314 Storage storage_{};
315};
316
317template <typename E>
318class Expected<void, E> {
319public:
320 using value_type = void;
321 using error_type = E;
323
324 constexpr Expected() noexcept = default;
325 constexpr Expected(const Unexpected<E>& error) : has_value_(false), error_(error.error()) {}
326 constexpr Expected(Unexpected<E>&& error) : has_value_(false), error_(std::move(error).error()) {}
327
328 constexpr Expected(const Expected&) = default;
329 constexpr Expected(Expected&&) noexcept = default;
330 constexpr Expected& operator=(const Expected&) = default;
331 constexpr Expected& operator=(Expected&&) noexcept = default;
332 ~Expected() = default;
333
334 [[nodiscard]] constexpr bool has_value() const noexcept {
335 return has_value_;
336 }
337
338 [[nodiscard]] constexpr explicit operator bool() const noexcept {
339 return has_value();
340 }
341
342 constexpr void value() const {
343 if (!has_value_) {
345 }
346 }
347
348 [[nodiscard]] constexpr E& error() & {
349 if (has_value_) {
351 }
352 return error_;
353 }
354
355 [[nodiscard]] constexpr const E& error() const& {
356 if (has_value_) {
358 }
359 return error_;
360 }
361
362 [[nodiscard]] constexpr E&& error() && {
363 if (has_value_) {
365 }
366 return std::move(error_);
367 }
368
369 [[nodiscard]] constexpr const E&& error() const&& {
370 if (has_value_) {
372 }
373 return std::move(error_);
374 }
375
376private:
377 bool has_value_ = true;
378 E error_{};
379};
380
381} // namespace purify
constexpr E && error() &&
Definition expected.hpp:362
constexpr const E && error() const &&
Definition expected.hpp:369
constexpr Expected(Expected &&) noexcept=default
constexpr Expected(Unexpected< E > &&error)
Definition expected.hpp:326
constexpr Expected() noexcept=default
constexpr const E & error() const &
Definition expected.hpp:355
constexpr E & error() &
Definition expected.hpp:348
constexpr Expected(const Expected &)=default
constexpr void value() const
Definition expected.hpp:342
Purify result carrier that either holds a value or an error.
Definition expected.hpp:64
Expected(const Expected &other)
Definition expected.hpp:93
Expected(Unexpected< E > &&error)
Definition expected.hpp:88
Expected(const Unexpected< E > &error)
Definition expected.hpp:84
const T & operator*() const &
Definition expected.hpp:182
const T && operator*() const &&
Definition expected.hpp:190
E && error() &&
Definition expected.hpp:244
const E && error() const &&
Definition expected.hpp:251
const T && value() const &&
Definition expected.hpp:223
Expected(const T &value)
Definition expected.hpp:76
Expected(T &&value)
Definition expected.hpp:80
T && value() &&
Definition expected.hpp:216
Expected & operator=(const Expected &other)
Definition expected.hpp:112
T && operator*() &&
Definition expected.hpp:186
const E & error() const &
Definition expected.hpp:237
Expected & operator=(Expected &&other) noexcept(std::is_nothrow_move_assignable_v< T > &&std::is_nothrow_move_assignable_v< E > &&std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_move_constructible_v< E >)
Definition expected.hpp:137
bool has_value() const noexcept
Definition expected.hpp:170
Expected(Expected &&other) noexcept(std::is_nothrow_move_constructible_v< T > &&std::is_nothrow_move_constructible_v< E >)
Definition expected.hpp:102
const T * operator->() const
Definition expected.hpp:198
const T & value() const &
Definition expected.hpp:209
constexpr const E & error() const &noexcept
Definition expected.hpp:46
constexpr E & error() &noexcept
Definition expected.hpp:42
constexpr Unexpected(const E &error)
Definition expected.hpp:39
constexpr E && error() &&noexcept
Definition expected.hpp:50
constexpr const E && error() const &&noexcept
Definition expected.hpp:54
constexpr Unexpected(E &&error)
Definition expected.hpp:40
const char * what() const noexcept override
Definition expected.hpp:31
Definition api.hpp:21
constexpr unexpect_t unexpect
Definition expected.hpp:26
constexpr unexpect_t() noexcept=default