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