1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_TYPES_EXPECTED_INTERNAL_H_
6 #define BASE_TYPES_EXPECTED_INTERNAL_H_
7
8 // IWYU pragma: private, include "base/types/expected.h"
9 #include <concepts>
10 #include <functional>
11 #include <type_traits>
12 #include <utility>
13
14 #include "base/check.h"
15 #include "base/template_util.h"
16 #include "third_party/abseil-cpp/absl/types/variant.h"
17 #include "third_party/abseil-cpp/absl/utility/utility.h"
18
19 // This header defines type traits and aliases used for the implementation of
20 // base::expected.
21 namespace base {
22
23 template <typename T>
24 class ok;
25
26 template <typename E>
27 class unexpected;
28
29 struct unexpect_t {
30 explicit unexpect_t() = default;
31 };
32
33 // in-place construction of unexpected values
34 inline constexpr unexpect_t unexpect{};
35
36 template <typename T, typename E>
37 class expected;
38
39 namespace internal {
40
41 template <typename T>
42 inline constexpr bool UnderlyingIsOk = false;
43 template <typename T>
44 inline constexpr bool UnderlyingIsOk<ok<T>> = true;
45 template <typename T>
46 inline constexpr bool IsOk = UnderlyingIsOk<remove_cvref_t<T>>;
47
48 template <typename T>
49 inline constexpr bool UnderlyingIsUnexpected = false;
50 template <typename E>
51 inline constexpr bool UnderlyingIsUnexpected<unexpected<E>> = true;
52 template <typename T>
53 inline constexpr bool IsUnexpected = UnderlyingIsUnexpected<remove_cvref_t<T>>;
54
55 template <typename T>
56 inline constexpr bool UnderlyingIsExpected = false;
57 template <typename T, typename E>
58 inline constexpr bool UnderlyingIsExpected<expected<T, E>> = true;
59 template <typename T>
60 inline constexpr bool IsExpected = UnderlyingIsExpected<remove_cvref_t<T>>;
61
62 template <typename T, typename U>
63 inline constexpr bool IsConstructibleOrConvertible =
64 std::is_constructible_v<T, U> || std::is_convertible_v<U, T>;
65
66 template <typename T, typename U>
67 inline constexpr bool IsAnyConstructibleOrConvertible =
68 IsConstructibleOrConvertible<T, U&> ||
69 IsConstructibleOrConvertible<T, U&&> ||
70 IsConstructibleOrConvertible<T, const U&> ||
71 IsConstructibleOrConvertible<T, const U&&>;
72
73 // Checks whether a given expected<U, G> can be converted into another
74 // expected<T, E>. Used inside expected's conversion constructors. UF and GF are
75 // the forwarded versions of U and G, e.g. UF is const U& for the converting
76 // copy constructor and U for the converting move constructor. Similarly for GF.
77 // ExUG is used for convenience, and not expected to be passed explicitly.
78 // See https://eel.is/c++draft/expected#lib:expected,constructor___
79 template <typename T,
80 typename E,
81 typename UF,
82 typename GF,
83 typename ExUG = expected<remove_cvref_t<UF>, remove_cvref_t<GF>>>
84 inline constexpr bool IsValidConversion =
85 std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
86 !IsAnyConstructibleOrConvertible<T, ExUG> &&
87 !IsAnyConstructibleOrConvertible<unexpected<E>, ExUG>;
88
89 // Checks whether a given expected<U, G> can be converted into another
90 // expected<T, E> when T is a void type. Used inside expected<void>'s conversion
91 // constructors. GF is the forwarded versions of G, e.g. GF is const G& for the
92 // converting copy constructor and G for the converting move constructor. ExUG
93 // is used for convenience, and not expected to be passed explicitly. See
94 // https://eel.is/c++draft/expected#lib:expected%3cvoid%3e,constructor___
95 template <typename E,
96 typename U,
97 typename GF,
98 typename ExUG = expected<U, remove_cvref_t<GF>>>
99 inline constexpr bool IsValidVoidConversion =
100 std::is_void_v<U> && std::is_constructible_v<E, GF> &&
101 !IsAnyConstructibleOrConvertible<unexpected<E>, ExUG>;
102
103 // Checks whether expected<T, E> can be constructed from a value of type U.
104 template <typename T, typename E, typename U>
105 inline constexpr bool IsValidValueConstruction =
106 std::is_constructible_v<T, U> &&
107 !std::is_same_v<remove_cvref_t<U>, absl::in_place_t> &&
108 !std::is_same_v<remove_cvref_t<U>, expected<T, E>> && !IsOk<U> &&
109 !IsUnexpected<U>;
110
111 template <typename T, typename U>
112 inline constexpr bool IsOkValueConstruction =
113 !std::is_same_v<remove_cvref_t<U>, ok<T>> &&
114 !std::is_same_v<remove_cvref_t<U>, absl::in_place_t> &&
115 std::is_constructible_v<T, U>;
116
117 template <typename T, typename U>
118 inline constexpr bool IsUnexpectedValueConstruction =
119 !std::is_same_v<remove_cvref_t<U>, unexpected<T>> &&
120 !std::is_same_v<remove_cvref_t<U>, absl::in_place_t> &&
121 std::is_constructible_v<T, U>;
122
123 template <typename T, typename E, typename U>
124 inline constexpr bool IsValueAssignment =
125 !std::is_same_v<expected<T, E>, remove_cvref_t<U>> && !IsOk<U> &&
126 !IsUnexpected<U> && std::is_constructible_v<T, U> &&
127 std::is_assignable_v<T&, U>;
128
129 template <typename T, typename E>
130 class ExpectedImpl {
131 public:
132 static constexpr size_t kValIdx = 1;
133 static constexpr size_t kErrIdx = 2;
134 static constexpr absl::in_place_index_t<1> kValTag{};
135 static constexpr absl::in_place_index_t<2> kErrTag{};
136
137 template <typename U, typename G>
138 friend class ExpectedImpl;
139
ExpectedImpl()140 constexpr ExpectedImpl() noexcept
141 requires(std::default_initializable<T>)
142 : data_(kValTag) {}
ExpectedImpl(const ExpectedImpl & rhs)143 constexpr ExpectedImpl(const ExpectedImpl& rhs) noexcept : data_(rhs.data_) {
144 CHECK(!rhs.is_moved_from());
145 }
ExpectedImpl(ExpectedImpl && rhs)146 constexpr ExpectedImpl(ExpectedImpl&& rhs) noexcept
147 : data_(std::move(rhs.data_)) {
148 CHECK(!rhs.is_moved_from());
149 rhs.set_is_moved_from();
150 }
151
152 template <typename U, typename G>
ExpectedImpl(const ExpectedImpl<U,G> & rhs)153 constexpr explicit ExpectedImpl(const ExpectedImpl<U, G>& rhs) noexcept {
154 if (rhs.has_value()) {
155 emplace_value(rhs.value());
156 } else {
157 emplace_error(rhs.error());
158 }
159 }
160
161 template <typename U, typename G>
ExpectedImpl(ExpectedImpl<U,G> && rhs)162 constexpr explicit ExpectedImpl(ExpectedImpl<U, G>&& rhs) noexcept {
163 if (rhs.has_value()) {
164 emplace_value(std::move(rhs.value()));
165 } else {
166 emplace_error(std::move(rhs.error()));
167 }
168 rhs.set_is_moved_from();
169 }
170
171 template <typename... Args>
ExpectedImpl(decltype (kValTag),Args &&...args)172 constexpr explicit ExpectedImpl(decltype(kValTag), Args&&... args) noexcept
173 : data_(kValTag, std::forward<Args>(args)...) {}
174
175 template <typename U, typename... Args>
ExpectedImpl(decltype (kValTag),std::initializer_list<U> il,Args &&...args)176 constexpr explicit ExpectedImpl(decltype(kValTag),
177 std::initializer_list<U> il,
178 Args&&... args) noexcept
179 : data_(kValTag, il, std::forward<Args>(args)...) {}
180
181 template <typename... Args>
ExpectedImpl(decltype (kErrTag),Args &&...args)182 constexpr explicit ExpectedImpl(decltype(kErrTag), Args&&... args) noexcept
183 : data_(kErrTag, std::forward<Args>(args)...) {}
184
185 template <typename U, typename... Args>
ExpectedImpl(decltype (kErrTag),std::initializer_list<U> il,Args &&...args)186 constexpr explicit ExpectedImpl(decltype(kErrTag),
187 std::initializer_list<U> il,
188 Args&&... args) noexcept
189 : data_(kErrTag, il, std::forward<Args>(args)...) {}
190
191 constexpr ExpectedImpl& operator=(const ExpectedImpl& rhs) noexcept {
192 CHECK(!rhs.is_moved_from());
193 data_ = rhs.data_;
194 return *this;
195 }
196
197 constexpr ExpectedImpl& operator=(ExpectedImpl&& rhs) noexcept {
198 CHECK(!rhs.is_moved_from());
199 data_ = std::move(rhs.data_);
200 rhs.set_is_moved_from();
201 return *this;
202 }
203
204 template <typename... Args>
emplace_value(Args &&...args)205 constexpr T& emplace_value(Args&&... args) noexcept {
206 return data_.template emplace<kValIdx>(std::forward<Args>(args)...);
207 }
208
209 template <typename U, typename... Args>
emplace_value(std::initializer_list<U> il,Args &&...args)210 constexpr T& emplace_value(std::initializer_list<U> il,
211 Args&&... args) noexcept {
212 return data_.template emplace<kValIdx>(il, std::forward<Args>(args)...);
213 }
214
215 template <typename... Args>
emplace_error(Args &&...args)216 constexpr E& emplace_error(Args&&... args) noexcept {
217 return data_.template emplace<kErrIdx>(std::forward<Args>(args)...);
218 }
219
220 template <typename U, typename... Args>
emplace_error(std::initializer_list<U> il,Args &&...args)221 constexpr E& emplace_error(std::initializer_list<U> il,
222 Args&&... args) noexcept {
223 return data_.template emplace<kErrIdx>(il, std::forward<Args>(args)...);
224 }
225
swap(ExpectedImpl & rhs)226 void swap(ExpectedImpl& rhs) noexcept {
227 CHECK(!is_moved_from());
228 CHECK(!rhs.is_moved_from());
229 data_.swap(rhs.data_);
230 }
231
has_value()232 constexpr bool has_value() const noexcept {
233 CHECK(!is_moved_from());
234 return data_.index() == kValIdx;
235 }
236
237 // Note: No `CHECK()` here and below, since absl::get already checks that
238 // the passed in index is active.
value()239 constexpr T& value() noexcept { return absl::get<kValIdx>(data_); }
value()240 constexpr const T& value() const noexcept {
241 return absl::get<kValIdx>(data_);
242 }
243
error()244 constexpr E& error() noexcept { return absl::get<kErrIdx>(data_); }
error()245 constexpr const E& error() const noexcept {
246 return absl::get<kErrIdx>(data_);
247 }
248
249 private:
250 static constexpr size_t kNulIdx = 0;
251 static_assert(kNulIdx != kValIdx);
252 static_assert(kNulIdx != kErrIdx);
253
is_moved_from()254 constexpr bool is_moved_from() const noexcept {
255 return data_.index() == kNulIdx;
256 }
257
set_is_moved_from()258 constexpr void set_is_moved_from() noexcept {
259 data_.template emplace<kNulIdx>();
260 }
261
262 absl::variant<absl::monostate, T, E> data_;
263 };
264
265 template <typename Exp, typename F>
AndThen(Exp && exp,F && f)266 constexpr auto AndThen(Exp&& exp, F&& f) noexcept {
267 using T = remove_cvref_t<decltype(exp.value())>;
268 using E = remove_cvref_t<decltype(exp.error())>;
269
270 auto invoke_f = [&]() -> decltype(auto) {
271 if constexpr (!std::is_void_v<T>) {
272 return std::invoke(std::forward<F>(f), std::forward<Exp>(exp).value());
273 } else {
274 return std::invoke(std::forward<F>(f));
275 }
276 };
277
278 using U = decltype(invoke_f());
279 static_assert(internal::IsExpected<U>,
280 "expected<T, E>::and_then: Result of f() must be a "
281 "specialization of expected");
282 static_assert(
283 std::is_same_v<typename U::error_type, E>,
284 "expected<T, E>::and_then: Result of f() must have E as error_type");
285
286 return exp.has_value() ? invoke_f()
287 : U(unexpect, std::forward<Exp>(exp).error());
288 }
289
290 template <typename Exp, typename F>
OrElse(Exp && exp,F && f)291 constexpr auto OrElse(Exp&& exp, F&& f) noexcept {
292 using T = remove_cvref_t<decltype(exp.value())>;
293 using G = std::invoke_result_t<F, decltype(std::forward<Exp>(exp).error())>;
294
295 static_assert(internal::IsExpected<G>,
296 "expected<T, E>::or_else: Result of f() must be a "
297 "specialization of expected");
298 static_assert(
299 std::is_same_v<typename G::value_type, T>,
300 "expected<T, E>::or_else: Result of f() must have T as value_type");
301
302 if (!exp.has_value()) {
303 return std::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
304 }
305
306 if constexpr (!std::is_void_v<T>) {
307 return G(absl::in_place, std::forward<Exp>(exp).value());
308 } else {
309 return G();
310 }
311 }
312
313 template <typename Exp, typename F>
Transform(Exp && exp,F && f)314 constexpr auto Transform(Exp&& exp, F&& f) noexcept {
315 using T = remove_cvref_t<decltype(exp.value())>;
316 using E = remove_cvref_t<decltype(exp.error())>;
317
318 auto invoke_f = [&]() -> decltype(auto) {
319 if constexpr (!std::is_void_v<T>) {
320 return std::invoke(std::forward<F>(f), std::forward<Exp>(exp).value());
321 } else {
322 return std::invoke(std::forward<F>(f));
323 }
324 };
325
326 using U = std::remove_cv_t<decltype(invoke_f())>;
327 if constexpr (!std::is_void_v<U>) {
328 static_assert(!std::is_array_v<U>,
329 "expected<T, E>::transform: Result of f() should "
330 "not be an Array");
331 static_assert(!std::is_same_v<U, absl::in_place_t>,
332 "expected<T, E>::transform: Result of f() should "
333 "not be absl::in_place_t");
334 static_assert(!std::is_same_v<U, unexpect_t>,
335 "expected<T, E>::transform: Result of f() should "
336 "not be unexpect_t");
337 static_assert(!internal::IsOk<U>,
338 "expected<T, E>::transform: Result of f() should "
339 "not be a specialization of ok");
340 static_assert(!internal::IsUnexpected<U>,
341 "expected<T, E>::transform: Result of f() should "
342 "not be a specialization of unexpected");
343 static_assert(std::is_object_v<U>,
344 "expected<T, E>::transform: Result of f() should be "
345 "an object type");
346 }
347
348 if (!exp.has_value()) {
349 return expected<U, E>(unexpect, std::forward<Exp>(exp).error());
350 }
351
352 if constexpr (!std::is_void_v<U>) {
353 return expected<U, E>(absl::in_place, invoke_f());
354 } else {
355 invoke_f();
356 return expected<U, E>();
357 }
358 }
359
360 template <typename Exp, typename F>
TransformError(Exp && exp,F && f)361 constexpr auto TransformError(Exp&& exp, F&& f) noexcept {
362 using T = remove_cvref_t<decltype(exp.value())>;
363 using G = std::remove_cv_t<
364 std::invoke_result_t<F, decltype(std::forward<Exp>(exp).error())>>;
365
366 static_assert(
367 !std::is_array_v<G>,
368 "expected<T, E>::transform_error: Result of f() should not be an Array");
369 static_assert(!std::is_same_v<G, absl::in_place_t>,
370 "expected<T, E>::transform_error: Result of f() should not be "
371 "absl::in_place_t");
372 static_assert(!std::is_same_v<G, unexpect_t>,
373 "expected<T, E>::transform_error: Result of f() should not be "
374 "unexpect_t");
375 static_assert(!internal::IsOk<G>,
376 "expected<T, E>::transform_error: Result of f() should not be "
377 "a specialization of ok");
378 static_assert(!internal::IsUnexpected<G>,
379 "expected<T, E>::transform_error: Result of f() should not be "
380 "a specialization of unexpected");
381 static_assert(std::is_object_v<G>,
382 "expected<T, E>::transform_error: Result of f() should be an "
383 "object type");
384
385 if (!exp.has_value()) {
386 return expected<T, G>(
387 unexpect,
388 std::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()));
389 }
390
391 if constexpr (std::is_void_v<T>) {
392 return expected<T, G>();
393 } else {
394 return expected<T, G>(absl::in_place, std::forward<Exp>(exp).value());
395 }
396 }
397
398 } // namespace internal
399
400 } // namespace base
401
402 #endif // BASE_TYPES_EXPECTED_INTERNAL_H_
403