xref: /aosp_15_r20/external/cronet/base/types/expected_internal.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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