AsmGrader 0.0.0
Loading...
Searching...
No Matches
expected.hpp
Go to the documentation of this file.
1#pragma once
4
5#include <fmt/base.h>
6#include <fmt/format.h>
7#include <libassert/assert.hpp>
8
9#include <concepts>
10#include <string>
11#include <system_error>
12#include <type_traits>
13#include <utility>
14#include <variant>
15
16#include <unistd.h> // _exit
17
18namespace asmgrader {
19
21{
22};
23
24template <typename T = void, typename E = std::error_code>
33class [[nodiscard]] Expected
34{
35public:
36 using ExpectedT = T;
37 using ErrT = E;
38
39 constexpr Expected()
40 : data_{} {}
41
42 template <typename... Args>
43 explicit constexpr Expected(std::in_place_t /*unused*/, Args&&... args)
44 requires(!std::is_void_v<T> && std::constructible_from<ExpectedT, Args...>)
45 : data_{std::in_place_type<ExpectedT>, std::forward<Args>(args)...} {}
46
47 template <typename Tu>
48 constexpr Expected(Tu&& value) // NOLINT(*-explicit-*)
49 requires(!std::is_void_v<T> && std::convertible_to<Tu, T>)
50 : data_{std::forward<Tu>(value)} {}
51
52 template <typename Eu>
53 constexpr Expected(Eu&& error) // NOLINT(*-explicit-*)
54 requires(std::convertible_to<Eu, E> && !std::is_convertible_v<T, E> && !std::is_convertible_v<T, E>)
55 : data_{std::forward<Eu>(error)} {}
56
57 template <typename Eu>
58 constexpr Expected(UnexpectedT /*unused*/, Eu&& error) // NOLINT(*-explicit-*)
59 requires(std::convertible_to<Eu, E>)
60 : data_{std::forward<Eu>(error)} {}
61
62 constexpr void assert_val() { ASSERT(*this); }
63
64 constexpr bool has_value() const {
65 if constexpr (std::is_void_v<T>) {
66 return std::holds_alternative<std::monostate>(data_.data);
67 } else {
68 return std::holds_alternative<T>(data_.data);
69 }
70 }
71
72 constexpr bool has_error() const { return !has_value(); }
73
74 constexpr explicit operator bool() const { return has_value(); }
75
76 // TODO: clean this up. It seems like a whole void specialization would be required, but there MUST be a better way
77 template <typename U = T>
78 constexpr U& value()
79 requires(!std::is_void_v<U>)
80 {
81 return const_cast<U&>(const_cast<const Expected*>(this)->value());
82 }
83
84 template <typename U = T>
85 constexpr const U& value() const
86 requires(!std::is_void_v<U>)
87 {
88 static_assert(std::same_as<U, T>,
89 "Do not attempt to instantiate Expected<T,E>::value() for any type other than T");
90 DEBUG_ASSERT(has_value());
91 return std::get<U>(data_.data);
92 }
93
94 template <typename U = T>
95 constexpr void value() const
96 requires(std::is_void_v<U>)
97 {
98 static_assert(std::same_as<U, T>,
99 "Do not attempt to instantiate Expected<T,E>::value() for any type other than T");
100 DEBUG_ASSERT(has_value());
101 }
102
103 template <typename U = T>
104 constexpr U& operator*()
105 requires(!std::is_void_v<U>)
106 {
107 static_assert(std::same_as<U, T>,
108 "Do not attempt to instantiate Expected<T,E>::operator*() for any type other than T");
109 return value();
110 }
111
112 template <typename U = T>
113 constexpr const U& operator*() const
114 requires(!std::is_void_v<U>)
115 {
116 static_assert(std::same_as<U, T>,
117 "Do not attempt to instantiate Expected<T,E>::operator*() for any type other than T");
118 return value();
119 }
120
121 template <typename U = T>
122 constexpr U* operator->()
123 requires(!std::is_void_v<U>)
124 {
125 static_assert(std::same_as<U, T>,
126 "Do not attempt to instantiate Expected<T,E>::operator->() for any type other than T");
127 return &value();
128 }
129
130 template <typename U = T>
131 constexpr const U* operator->() const
132 requires(!std::is_void_v<U>)
133 {
134 static_assert(std::same_as<U, T>,
135 "Do not attempt to instantiate Expected<T,E>::operator->() for any type other than T");
136 return &value();
137 }
138
139 template <typename Tu>
140 constexpr T value_or(Tu&& default_value) const
141 requires(std::convertible_to<Tu, T>)
142 {
143 if (!has_value()) {
144 return static_cast<T>(std::forward<Tu>(default_value));
145 }
146 return std::get<T>(data_.data);
147 }
148
149 constexpr E error() const {
150 DEBUG_ASSERT(!has_value());
151 return std::get<E>(data_.data);
152 }
153
154 template <typename Eu>
155 constexpr E error_or(Eu&& default_value) const {
156 if (has_value()) {
157 return static_cast<E>(std::forward<Eu>(default_value));
158 }
159 return std::get<E>(data_.data);
160 }
161
162 template <typename Func>
163 constexpr Expected<std::invoke_result_t<Func, T>, E> transform(const Func& func) {
164 if (!has_value()) {
165 return error();
166 }
167
168 return func(value());
169 }
170
171private:
172 template <typename Td, typename Ed>
173 struct ExpectedData
174 {
175 std::variant<Td, Ed> data;
176 constexpr auto operator<=>(const ExpectedData& rhs) const = default;
177 };
178
179 template <typename Ed>
180 struct ExpectedData<void, Ed>
181 {
182 std::variant<std::monostate, Ed> data;
183 constexpr auto operator<=>(const ExpectedData& rhs) const = default;
184 };
185
186public:
187 constexpr auto operator<=>(const Expected& rhs) const = default;
188
189 // TODO: Figure out why it's necessary to define this
190 constexpr bool operator==(const Expected& rhs) const
191 requires(std::equality_comparable<E> && (std::is_void_v<T> || std::equality_comparable<T>))
192 {
193 return data_ == rhs.data_;
194 }
195
196 template <typename Tu>
197 constexpr bool operator==(const Tu& rhs) const
198 requires(!std::is_void_v<T> && !std::same_as<Tu, Expected> && std::equality_comparable_with<Tu, T>)
199 {
200 if (!has_value()) {
201 return false;
202 }
203
204 return value() == rhs;
205 }
206
207 template <typename Eu>
208 constexpr bool operator==(const Eu& rhs) const
209 requires(!std::same_as<Eu, Expected> && std::equality_comparable_with<Eu, E>)
210 {
211 if (has_value()) {
212 return false;
213 }
214
215 return error() == rhs;
216 }
217
218private:
219 ExpectedData<T, E> data_;
220};
221
222} // namespace asmgrader
223
224template <typename T, typename E>
225struct fmt::formatter<::asmgrader::Expected<T, E>> : ::asmgrader::DebugFormatter
226{
227
228 auto format(const ::asmgrader::Expected<T, E>& from, fmt::format_context& ctx) const {
229 // TODO: Use a `copy` algo. `ranges::copy` has stricter iter reqs than the `ctx.out()` type impls
230 return format_to(ctx.out(), "{}", format_impl(from));
231 }
232
233private:
234 std::string format_impl(const ::asmgrader::Expected<T, E>& from) const {
235 if (!from) {
236 if constexpr (formattable<E>) {
237 return fmt::format("Error({})", from.error());
238 } else {
239 return "Error(<unformattable>)";
240 }
241 }
242
243 if constexpr (std::same_as<T, void>) {
244 return "Expected(void)";
245 } else if constexpr (formattable<T>) {
246 return fmt::format("Expected({})", from.value());
247 } else {
248 return "Expected(<unformattable>)";
249 }
250 }
251};
std::variant wrapper for a partial implementation of C++23's expected type
Definition expected.hpp:34
E ErrT
Definition expected.hpp:37
constexpr const U * operator->() const
Definition expected.hpp:131
constexpr void assert_val()
Definition expected.hpp:62
constexpr bool operator==(const Expected &rhs) const
Definition expected.hpp:190
constexpr Expected(std::in_place_t, Args &&... args)
Definition expected.hpp:43
T ExpectedT
Definition expected.hpp:36
constexpr Expected(Tu &&value)
Definition expected.hpp:48
constexpr Expected< std::invoke_result_t< Func, T >, E > transform(const Func &func)
Definition expected.hpp:163
constexpr U & value()
Definition expected.hpp:78
constexpr T value_or(Tu &&default_value) const
Definition expected.hpp:140
constexpr U * operator->()
Definition expected.hpp:122
constexpr bool operator==(const Tu &rhs) const
Definition expected.hpp:197
constexpr E error() const
Definition expected.hpp:149
constexpr Expected(Eu &&error)
Definition expected.hpp:53
constexpr bool operator==(const Eu &rhs) const
Definition expected.hpp:208
constexpr void value() const
Definition expected.hpp:95
constexpr Expected(UnexpectedT, Eu &&error)
Definition expected.hpp:58
constexpr const U & operator*() const
Definition expected.hpp:113
constexpr E error_or(Eu &&default_value) const
Definition expected.hpp:155
constexpr const U & value() const
Definition expected.hpp:85
constexpr bool has_value() const
Definition expected.hpp:64
constexpr Expected()
Definition expected.hpp:39
constexpr U & operator*()
Definition expected.hpp:104
constexpr bool has_error() const
Definition expected.hpp:72
constexpr auto operator<=>(const Expected &rhs) const =default
Definition asm_buffer.hpp:19
Definition debug.hpp:9
Definition expected.hpp:21
auto format(const ::asmgrader::Expected< T, E > &from, fmt::format_context &ctx) const
Definition expected.hpp:228