• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <type_traits>
10 #include <utility>
11 
12 #include "base/check.h"
13 #include "base/template_util.h"
14 #include "third_party/abseil-cpp/absl/types/variant.h"
15 #include "third_party/abseil-cpp/absl/utility/utility.h"
16 
17 // This header defines type traits and aliases used for the implementation of
18 // base::expected.
19 namespace base {
20 
21 template <typename T, bool = std::is_void_v<T>>
22 class ok;
23 
24 template <typename E>
25 class unexpected;
26 
27 struct unexpect_t {
28   explicit unexpect_t() = default;
29 };
30 
31 // in-place construction of unexpected values
32 inline constexpr unexpect_t unexpect{};
33 
34 template <typename T, typename E, bool = std::is_void_v<T>>
35 class expected;
36 
37 namespace internal {
38 
39 template <typename T>
40 struct IsOk : std::false_type {};
41 
42 template <typename T>
43 struct IsOk<ok<T>> : std::true_type {};
44 
45 template <typename T>
46 struct IsUnexpected : std::false_type {};
47 
48 template <typename E>
49 struct IsUnexpected<unexpected<E>> : std::true_type {};
50 
51 template <typename T>
52 struct IsExpected : std::false_type {};
53 
54 template <typename T, typename E>
55 struct IsExpected<expected<T, E>> : std::true_type {};
56 
57 template <typename T, typename U>
58 struct IsConstructibleOrConvertible
59     : std::disjunction<std::is_constructible<T, U>, std::is_convertible<U, T>> {
60 };
61 
62 template <typename T, typename U>
63 struct IsAnyConstructibleOrConvertible
64     : std::disjunction<IsConstructibleOrConvertible<T, U&>,
65                        IsConstructibleOrConvertible<T, U&&>,
66                        IsConstructibleOrConvertible<T, const U&>,
67                        IsConstructibleOrConvertible<T, const U&&>> {};
68 
69 // Checks whether a given expected<U, G> can be converted into another
70 // expected<T, E>. Used inside expected's conversion constructors. UF and GF are
71 // the forwarded versions of U and G, e.g. UF is const U& for the converting
72 // copy constructor and U for the converting move constructor. Similarly for GF.
73 // ExUG is used for convenience, and not expected to be passed explicitly.
74 // See https://eel.is/c++draft/expected#lib:expected,constructor___
75 template <typename T,
76           typename E,
77           typename UF,
78           typename GF,
79           typename ExUG = expected<remove_cvref_t<UF>, remove_cvref_t<GF>>>
80 struct IsValidConversion
81     : std::conjunction<
82           std::is_constructible<T, UF>,
83           std::is_constructible<E, GF>,
84           std::negation<IsAnyConstructibleOrConvertible<T, ExUG>>,
85           std::negation<IsAnyConstructibleOrConvertible<unexpected<E>, ExUG>>> {
86 };
87 
88 // Checks whether a given expected<U, G> can be converted into another
89 // expected<T, E> when T is a void type. Used inside expected<void>'s conversion
90 // constructors. GF is the forwarded versions of G, e.g. GF is const G& for the
91 // converting copy constructor and G for the converting move constructor. ExUG
92 // is used for convenience, and not expected to be passed explicitly. See
93 // https://eel.is/c++draft/expected#lib:expected%3cvoid%3e,constructor___
94 template <typename E,
95           typename U,
96           typename GF,
97           typename ExUG = expected<U, remove_cvref_t<GF>>>
98 struct IsValidVoidConversion
99     : std::conjunction<
100           std::is_void<U>,
101           std::is_constructible<E, GF>,
102           std::negation<IsAnyConstructibleOrConvertible<unexpected<E>, ExUG>>> {
103 };
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 struct IsValidValueConstruction
108     : std::conjunction<
109           std::is_constructible<T, U>,
110           std::negation<std::is_same<remove_cvref_t<U>, absl::in_place_t>>,
111           std::negation<std::is_same<remove_cvref_t<U>, expected<T, E>>>,
112           std::negation<IsOk<remove_cvref_t<U>>>,
113           std::negation<IsUnexpected<remove_cvref_t<U>>>> {};
114 
115 template <typename T, typename E, typename UF, typename GF>
116 struct AreValueAndErrorConvertible
117     : std::conjunction<std::is_convertible<UF, T>, std::is_convertible<GF, E>> {
118 };
119 
120 template <typename T>
121 using EnableIfDefaultConstruction =
122     std::enable_if_t<std::is_default_constructible_v<T>, int>;
123 
124 template <typename T, typename E, typename UF, typename GF>
125 using EnableIfExplicitConversion = std::enable_if_t<
126     std::conjunction_v<
127         IsValidConversion<T, E, UF, GF>,
128         std::negation<AreValueAndErrorConvertible<T, E, UF, GF>>>,
129     int>;
130 
131 template <typename T, typename E, typename UF, typename GF>
132 using EnableIfImplicitConversion = std::enable_if_t<
133     std::conjunction_v<IsValidConversion<T, E, UF, GF>,
134                        AreValueAndErrorConvertible<T, E, UF, GF>>,
135     int>;
136 
137 template <typename E, typename U, typename GF>
138 using EnableIfExplicitVoidConversion = std::enable_if_t<
139     std::conjunction_v<IsValidVoidConversion<E, U, GF>,
140                        std::negation<std::is_convertible<GF, E>>>,
141     int>;
142 
143 template <typename E, typename U, typename GF>
144 using EnableIfImplicitVoidConversion =
145     std::enable_if_t<std::conjunction_v<IsValidVoidConversion<E, U, GF>,
146                                         std::is_convertible<GF, E>>,
147                      int>;
148 
149 template <typename T, typename U>
150 using EnableIfOkValueConstruction = std::enable_if_t<
151     std::conjunction_v<
152         std::negation<std::is_same<remove_cvref_t<U>, ok<T>>>,
153         std::negation<std::is_same<remove_cvref_t<U>, absl::in_place_t>>,
154         std::is_constructible<T, U>>,
155     int>;
156 
157 template <typename T, typename U>
158 using EnableIfUnexpectedValueConstruction = std::enable_if_t<
159     std::conjunction_v<
160         std::negation<std::is_same<remove_cvref_t<U>, unexpected<T>>>,
161         std::negation<std::is_same<remove_cvref_t<U>, absl::in_place_t>>,
162         std::is_constructible<T, U>>,
163     int>;
164 
165 template <typename T, typename E, typename U>
166 using EnableIfExplicitValueConstruction = std::enable_if_t<
167     std::conjunction_v<
168         IsValidValueConstruction<T, E, U>,
169         std::disjunction<std::negation<std::is_convertible<U, T>>,
170                          std::is_convertible<U, E>>>,
171     int>;
172 
173 template <typename T, typename E, typename U>
174 using EnableIfImplicitValueConstruction = std::enable_if_t<
175     std::conjunction_v<
176         IsValidValueConstruction<T, E, U>,
177         std::conjunction<std::is_convertible<U, T>,
178                          std::negation<std::is_convertible<U, E>>>>,
179     int>;
180 
181 template <typename T, typename U>
182 using EnableIfExplicitConstruction = std::enable_if_t<
183     std::conjunction_v<std::is_constructible<T, U>,
184                        std::negation<std::is_convertible<U, T>>>,
185     int>;
186 
187 template <typename T, typename U>
188 using EnableIfImplicitConstruction = std::enable_if_t<
189     std::conjunction_v<std::is_constructible<T, U>, std::is_convertible<U, T>>,
190     int>;
191 
192 template <typename T, typename E, typename U>
193 using EnableIfValueAssignment = std::enable_if_t<
194     std::conjunction_v<
195         std::negation<std::is_same<expected<T, E>, remove_cvref_t<U>>>,
196         std::negation<IsOk<remove_cvref_t<U>>>,
197         std::negation<IsUnexpected<remove_cvref_t<U>>>,
198         std::is_constructible<T, U>,
199         std::is_assignable<T&, U>>,
200     int>;
201 
202 template <typename T>
203 using EnableIfCopyConstructible =
204     std::enable_if_t<std::is_copy_constructible_v<T>, int>;
205 
206 template <typename T>
207 using EnableIfMoveConstructible =
208     std::enable_if_t<std::is_move_constructible_v<T>, int>;
209 
210 template <typename T>
211 using EnableIfNotVoid = std::enable_if_t<std::negation_v<std::is_void<T>>, int>;
212 
213 template <typename T, typename E>
214 class ExpectedImpl {
215  public:
216   static constexpr size_t kValIdx = 1;
217   static constexpr size_t kErrIdx = 2;
218   static constexpr absl::in_place_index_t<1> kValTag{};
219   static constexpr absl::in_place_index_t<2> kErrTag{};
220 
221   template <typename U, typename G>
222   friend class ExpectedImpl;
223 
224   template <typename LazyT = T, EnableIfDefaultConstruction<LazyT> = 0>
225   constexpr ExpectedImpl() noexcept : data_(kValTag) {}
226   constexpr ExpectedImpl(const ExpectedImpl& rhs) noexcept : data_(rhs.data_) {
227     CHECK(!rhs.is_moved_from());
228   }
229   constexpr ExpectedImpl(ExpectedImpl&& rhs) noexcept
230       : data_(std::move(rhs.data_)) {
231     CHECK(!rhs.is_moved_from());
232     rhs.set_is_moved_from();
233   }
234 
235   template <typename U, typename G>
236   constexpr explicit ExpectedImpl(const ExpectedImpl<U, G>& rhs) noexcept {
237     if (rhs.has_value()) {
238       emplace_value(rhs.value());
239     } else {
240       emplace_error(rhs.error());
241     }
242   }
243 
244   template <typename U, typename G>
245   constexpr explicit ExpectedImpl(ExpectedImpl<U, G>&& rhs) noexcept {
246     if (rhs.has_value()) {
247       emplace_value(std::move(rhs.value()));
248     } else {
249       emplace_error(std::move(rhs.error()));
250     }
251     rhs.set_is_moved_from();
252   }
253 
254   template <typename... Args>
255   constexpr explicit ExpectedImpl(decltype(kValTag), Args&&... args) noexcept
256       : data_(kValTag, std::forward<Args>(args)...) {}
257 
258   template <typename U, typename... Args>
259   constexpr explicit ExpectedImpl(decltype(kValTag),
260                                   std::initializer_list<U> il,
261                                   Args&&... args) noexcept
262       : data_(kValTag, il, std::forward<Args>(args)...) {}
263 
264   template <typename... Args>
265   constexpr explicit ExpectedImpl(decltype(kErrTag), Args&&... args) noexcept
266       : data_(kErrTag, std::forward<Args>(args)...) {}
267 
268   template <typename U, typename... Args>
269   constexpr explicit ExpectedImpl(decltype(kErrTag),
270                                   std::initializer_list<U> il,
271                                   Args&&... args) noexcept
272       : data_(kErrTag, il, std::forward<Args>(args)...) {}
273 
274   constexpr ExpectedImpl& operator=(const ExpectedImpl& rhs) noexcept {
275     CHECK(!rhs.is_moved_from());
276     data_ = rhs.data_;
277     return *this;
278   }
279 
280   constexpr ExpectedImpl& operator=(ExpectedImpl&& rhs) noexcept {
281     CHECK(!rhs.is_moved_from());
282     data_ = std::move(rhs.data_);
283     rhs.set_is_moved_from();
284     return *this;
285   }
286 
287   template <typename... Args>
288   constexpr T& emplace_value(Args&&... args) noexcept {
289     return data_.template emplace<kValIdx>(std::forward<Args>(args)...);
290   }
291 
292   template <typename U, typename... Args>
293   constexpr T& emplace_value(std::initializer_list<U> il,
294                              Args&&... args) noexcept {
295     return data_.template emplace<kValIdx>(il, std::forward<Args>(args)...);
296   }
297 
298   template <typename... Args>
299   constexpr E& emplace_error(Args&&... args) noexcept {
300     return data_.template emplace<kErrIdx>(std::forward<Args>(args)...);
301   }
302 
303   template <typename U, typename... Args>
304   constexpr E& emplace_error(std::initializer_list<U> il,
305                              Args&&... args) noexcept {
306     return data_.template emplace<kErrIdx>(il, std::forward<Args>(args)...);
307   }
308 
309   void swap(ExpectedImpl& rhs) noexcept {
310     CHECK(!is_moved_from());
311     CHECK(!rhs.is_moved_from());
312     data_.swap(rhs.data_);
313   }
314 
315   constexpr bool has_value() const noexcept {
316     CHECK(!is_moved_from());
317     return data_.index() == kValIdx;
318   }
319 
320   // Note: No `CHECK()` here and below, since absl::get already checks that
321   // the passed in index is active.
322   constexpr T& value() noexcept { return absl::get<kValIdx>(data_); }
323   constexpr const T& value() const noexcept {
324     return absl::get<kValIdx>(data_);
325   }
326 
327   constexpr E& error() noexcept { return absl::get<kErrIdx>(data_); }
328   constexpr const E& error() const noexcept {
329     return absl::get<kErrIdx>(data_);
330   }
331 
332  private:
333   static constexpr size_t kNulIdx = 0;
334   static_assert(kNulIdx != kValIdx);
335   static_assert(kNulIdx != kErrIdx);
336 
337   constexpr bool is_moved_from() const noexcept {
338     return data_.index() == kNulIdx;
339   }
340 
341   constexpr void set_is_moved_from() noexcept {
342     data_.template emplace<kNulIdx>();
343   }
344 
345   absl::variant<absl::monostate, T, E> data_;
346 };
347 
348 template <typename Exp, typename F>
349 constexpr auto AndThen(Exp&& exp, F&& f) noexcept {
350   using T = remove_cvref_t<decltype(exp.value())>;
351   using E = remove_cvref_t<decltype(exp.error())>;
352 
353   auto invoke_f = [&]() -> decltype(auto) {
354     if constexpr (!std::is_void_v<T>) {
355       return std::invoke(std::forward<F>(f), std::forward<Exp>(exp).value());
356     } else {
357       return std::invoke(std::forward<F>(f));
358     }
359   };
360 
361   using U = remove_cvref_t<decltype(invoke_f())>;
362   static_assert(internal::IsExpected<U>::value,
363                 "expected<T, E>::and_then: Result of f() must be a "
364                 "specialization of expected");
365   static_assert(
366       std::is_same_v<typename U::error_type, E>,
367       "expected<T, E>::and_then: Result of f() must have E as error_type");
368 
369   return exp.has_value() ? invoke_f()
370                          : U(unexpect, std::forward<Exp>(exp).error());
371 }
372 
373 template <typename Exp, typename F>
374 constexpr auto OrElse(Exp&& exp, F&& f) noexcept {
375   using T = remove_cvref_t<decltype(exp.value())>;
376   using G = remove_cvref_t<
377       std::invoke_result_t<F, decltype(std::forward<Exp>(exp).error())>>;
378 
379   static_assert(internal::IsExpected<G>::value,
380                 "expected<T, E>::or_else: Result of f() must be a "
381                 "specialization of expected");
382   static_assert(
383       std::is_same_v<typename G::value_type, T>,
384       "expected<T, E>::or_else: Result of f() must have T as value_type");
385 
386   if (!exp.has_value()) {
387     return std::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
388   }
389 
390   if constexpr (!std::is_void_v<T>) {
391     return G(absl::in_place, std::forward<Exp>(exp).value());
392   } else {
393     return G();
394   }
395 }
396 
397 template <typename Exp, typename F>
398 constexpr auto Transform(Exp&& exp, F&& f) noexcept {
399   using T = remove_cvref_t<decltype(exp.value())>;
400   using E = remove_cvref_t<decltype(exp.error())>;
401 
402   auto invoke_f = [&]() -> decltype(auto) {
403     if constexpr (!std::is_void_v<T>) {
404       return std::invoke(std::forward<F>(f), std::forward<Exp>(exp).value());
405     } else {
406       return std::invoke(std::forward<F>(f));
407     }
408   };
409 
410   using U = std::remove_cv_t<decltype(invoke_f())>;
411   if constexpr (!std::is_void_v<U>) {
412     static_assert(!std::is_array_v<U>,
413                   "expected<T, E>::transform: Result of f() should "
414                   "not be an Array");
415     static_assert(!std::is_same_v<U, absl::in_place_t>,
416                   "expected<T, E>::transform: Result of f() should "
417                   "not be absl::in_place_t");
418     static_assert(!std::is_same_v<U, unexpect_t>,
419                   "expected<T, E>::transform: Result of f() should "
420                   "not be unexpect_t");
421     static_assert(!internal::IsOk<U>::value,
422                   "expected<T, E>::transform: Result of f() should "
423                   "not be a specialization of ok");
424     static_assert(!internal::IsUnexpected<U>::value,
425                   "expected<T, E>::transform: Result of f() should "
426                   "not be a specialization of unexpected");
427     static_assert(std::is_object_v<U>,
428                   "expected<T, E>::transform: Result of f() should be "
429                   "an object type");
430   }
431 
432   if (!exp.has_value()) {
433     return expected<U, E>(unexpect, std::forward<Exp>(exp).error());
434   }
435 
436   if constexpr (!std::is_void_v<U>) {
437     return expected<U, E>(absl::in_place, invoke_f());
438   } else {
439     invoke_f();
440     return expected<U, E>();
441   }
442 }
443 
444 template <typename Exp, typename F>
445 constexpr auto TransformError(Exp&& exp, F&& f) noexcept {
446   using T = remove_cvref_t<decltype(exp.value())>;
447   using G = std::remove_cv_t<
448       std::invoke_result_t<F, decltype(std::forward<Exp>(exp).error())>>;
449 
450   static_assert(
451       !std::is_array_v<G>,
452       "expected<T, E>::transform_error: Result of f() should not be an Array");
453   static_assert(!std::is_same_v<G, absl::in_place_t>,
454                 "expected<T, E>::transform_error: Result of f() should not be "
455                 "absl::in_place_t");
456   static_assert(!std::is_same_v<G, unexpect_t>,
457                 "expected<T, E>::transform_error: Result of f() should not be "
458                 "unexpect_t");
459   static_assert(!internal::IsOk<G>::value,
460                 "expected<T, E>::transform_error: Result of f() should not be "
461                 "a specialization of ok");
462   static_assert(!internal::IsUnexpected<G>::value,
463                 "expected<T, E>::transform_error: Result of f() should not be "
464                 "a specialization of unexpected");
465   static_assert(std::is_object_v<G>,
466                 "expected<T, E>::transform_error: Result of f() should be an "
467                 "object type");
468 
469   if (!exp.has_value()) {
470     return expected<T, G>(
471         unexpect,
472         std::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()));
473   }
474 
475   if constexpr (std::is_void_v<T>) {
476     return expected<T, G>();
477   } else {
478     return expected<T, G>(absl::in_place, std::forward<Exp>(exp).value());
479   }
480 }
481 
482 }  // namespace internal
483 
484 }  // namespace base
485 
486 #endif  // BASE_TYPES_EXPECTED_INTERNAL_H_
487