xref: /aosp_15_r20/external/pigweed/pw_result/public/pw_result/internal/result_internal.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <type_traits>
17 #include <utility>
18 
19 #include "pw_assert/assert.h"
20 #include "pw_status/status.h"
21 
22 namespace pw {
23 
24 template <typename T>
25 class [[nodiscard]] Result;
26 
27 namespace internal_result {
28 
29 // Detects whether `U` has conversion operator to `Result<T>`, i.e. `operator
30 // Result<T>()`.
31 template <typename T, typename U, typename = void>
32 struct HasConversionOperatorToResult : std::false_type {};
33 
34 template <typename T, typename U>
35 void test(char (*)[sizeof(std::declval<U>().operator Result<T>())]);
36 
37 template <typename T, typename U>
38 struct HasConversionOperatorToResult<T, U, decltype(test<T, U>(0))>
39     : std::true_type {};
40 
41 // Detects whether `T` is constructible or convertible from `Result<U>`.
42 template <typename T, typename U>
43 using IsConstructibleOrConvertibleFromResult =
44     std::disjunction<std::is_constructible<T, Result<U>&>,
45                      std::is_constructible<T, const Result<U>&>,
46                      std::is_constructible<T, Result<U>&&>,
47                      std::is_constructible<T, const Result<U>&&>,
48                      std::is_convertible<Result<U>&, T>,
49                      std::is_convertible<const Result<U>&, T>,
50                      std::is_convertible<Result<U>&&, T>,
51                      std::is_convertible<const Result<U>&&, T>>;
52 
53 // Detects whether `T` is constructible or convertible or assignable from
54 // `Result<U>`.
55 template <typename T, typename U>
56 using IsConstructibleOrConvertibleOrAssignableFromResult =
57     std::disjunction<IsConstructibleOrConvertibleFromResult<T, U>,
58                      std::is_assignable<T&, Result<U>&>,
59                      std::is_assignable<T&, const Result<U>&>,
60                      std::is_assignable<T&, Result<U>&&>,
61                      std::is_assignable<T&, const Result<U>&&>>;
62 
63 // Detects whether direct initializing `Result<T>` from `U` is ambiguous, i.e.
64 // when `U` is `Result<V>` and `T` is constructible or convertible from `V`.
65 template <typename T, typename U>
66 struct IsDirectInitializationAmbiguous
67     : public std::conditional_t<
68           std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, U>::value,
69           std::false_type,
70           IsDirectInitializationAmbiguous<
71               T,
72               std::remove_cv_t<std::remove_reference_t<U>>>> {};
73 
74 template <typename T, typename V>
75 struct IsDirectInitializationAmbiguous<T, Result<V>>
76     : public IsConstructibleOrConvertibleFromResult<T, V> {};
77 
78 // Checks against the constraints of the direction initialization, i.e. when
79 // `Result<T>::Result(U&&)` should participate in overload resolution.
80 template <typename T, typename U>
81 using IsDirectInitializationValid = std::disjunction<
82     // Short circuits if T is basically U.
83     std::is_same<T, std::remove_cv_t<std::remove_reference_t<U>>>,
84     std::negation<std::disjunction<
85         std::is_same<Result<T>, std::remove_cv_t<std::remove_reference_t<U>>>,
86         std::is_same<Status, std::remove_cv_t<std::remove_reference_t<U>>>,
87         std::is_same<std::in_place_t,
88                      std::remove_cv_t<std::remove_reference_t<U>>>,
89         IsDirectInitializationAmbiguous<T, U>>>>;
90 
91 // This trait detects whether `Result<T>::operator=(U&&)` is ambiguous, which
92 // is equivalent to whether all the following conditions are met:
93 // 1. `U` is `Result<V>`.
94 // 2. `T` is constructible and assignable from `V`.
95 // 3. `T` is constructible and assignable from `U` (i.e. `Result<V>`).
96 // For example, the following code is considered ambiguous:
97 // (`T` is `bool`, `U` is `Result<bool>`, `V` is `bool`)
98 //   Result<bool> s1 = true;  // s1.ok() && s1.ValueOrDie() == true
99 //   Result<bool> s2 = false;  // s2.ok() && s2.ValueOrDie() == false
100 //   s1 = s2;  // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`?
101 template <typename T, typename U>
102 struct IsForwardingAssignmentAmbiguous
103     : public std::conditional_t<
104           std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, U>::value,
105           std::false_type,
106           IsForwardingAssignmentAmbiguous<
107               T,
108               std::remove_cv_t<std::remove_reference_t<U>>>> {};
109 
110 template <typename T, typename U>
111 struct IsForwardingAssignmentAmbiguous<T, Result<U>>
112     : public IsConstructibleOrConvertibleOrAssignableFromResult<T, U> {};
113 
114 // Checks against the constraints of the forwarding assignment, i.e. whether
115 // `Result<T>::operator(U&&)` should participate in overload resolution.
116 template <typename T, typename U>
117 using IsForwardingAssignmentValid = std::disjunction<
118     // Short circuits if T is basically U.
119     std::is_same<T, std::remove_cv_t<std::remove_reference_t<U>>>,
120     std::negation<std::disjunction<
121         std::is_same<Result<T>, std::remove_cv_t<std::remove_reference_t<U>>>,
122         std::is_same<Status, std::remove_cv_t<std::remove_reference_t<U>>>,
123         std::is_same<std::in_place_t,
124                      std::remove_cv_t<std::remove_reference_t<U>>>,
125         IsForwardingAssignmentAmbiguous<T, U>>>>;
126 
127 // This trait is for determining if a given type is a Result.
128 template <typename T>
129 constexpr bool IsResult = false;
130 template <typename T>
131 constexpr bool IsResult<Result<T>> = true;
132 
133 // This trait determines the return type of a given function without const,
134 // volatile or reference qualifiers.
135 template <typename Fn, typename T>
136 using InvokeResultType =
137     std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<Fn, T>>>;
138 
139 PW_MODIFY_DIAGNOSTICS_PUSH();
140 PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
141 
142 // Construct an instance of T in `p` through placement new, passing Args... to
143 // the constructor.
144 // This abstraction is here mostly for the gcc performance fix.
145 template <typename T, typename... Args>
146 void PlacementNew(void* p, Args&&... args) {
147   new (p) T(std::forward<Args>(args)...);
148 }
149 
150 // Helper base class to hold the data and all operations.
151 // We move all this to a base class to allow mixing with the appropriate
152 // TraitsBase specialization.
153 //
154 // Pigweed addition: Specialize StatusOrData for trivially destructible types.
155 // This makes a Result usable in a constexpr statement.
156 //
157 // Note: in C++20, this entire file can be greatly simplfied with the requires
158 // statement.
159 template <typename T, bool = std::is_trivially_destructible<T>::value>
160 class StatusOrData;
161 
162 // Place the implementation of StatusOrData in a macro so it can be shared
163 // between both specializations.
164 #define PW_RESULT_STATUS_OR_DATA_IMPL                                          \
165   template <typename U, bool>                                                  \
166   friend class StatusOrData;                                                   \
167                                                                                \
168  public:                                                                       \
169   StatusOrData() = delete;                                                     \
170                                                                                \
171   constexpr StatusOrData(const StatusOrData& other)                            \
172       : status_(other.status_), unused_() {                                    \
173     if (other.ok()) {                                                          \
174       MakeValue(other.data_);                                                  \
175     }                                                                          \
176   }                                                                            \
177                                                                                \
178   constexpr StatusOrData(StatusOrData&& other) noexcept                        \
179       : status_(std::move(other.status_)), unused_() {                         \
180     if (other.ok()) {                                                          \
181       MakeValue(std::move(other.data_));                                       \
182     }                                                                          \
183   }                                                                            \
184                                                                                \
185   template <typename U>                                                        \
186   explicit constexpr StatusOrData(const StatusOrData<U>& other) {              \
187     if (other.ok()) {                                                          \
188       MakeValue(other.data_);                                                  \
189       status_ = OkStatus();                                                    \
190     } else {                                                                   \
191       status_ = other.status_;                                                 \
192     }                                                                          \
193   }                                                                            \
194                                                                                \
195   template <typename U>                                                        \
196   explicit constexpr StatusOrData(StatusOrData<U>&& other) {                   \
197     if (other.ok()) {                                                          \
198       MakeValue(std::move(other.data_));                                       \
199       status_ = OkStatus();                                                    \
200     } else {                                                                   \
201       status_ = std::move(other.status_);                                      \
202     }                                                                          \
203   }                                                                            \
204                                                                                \
205   template <typename... Args>                                                  \
206   explicit constexpr StatusOrData(std::in_place_t, Args&&... args)             \
207       : status_(), data_(std::forward<Args>(args)...) {}                       \
208                                                                                \
209   explicit constexpr StatusOrData(const T& value) : status_(), data_(value) {} \
210   explicit constexpr StatusOrData(T&& value)                                   \
211       : status_(), data_(std::move(value)) {}                                  \
212                                                                                \
213   template <typename U,                                                        \
214             std::enable_if_t<std::is_constructible<Status, U&&>::value, int> = \
215                 0>                                                             \
216   explicit constexpr StatusOrData(U&& v)                                       \
217       : status_(std::forward<U>(v)), unused_() {                               \
218     PW_ASSERT(!status_.ok());                                                  \
219   }                                                                            \
220                                                                                \
221   constexpr StatusOrData& operator=(const StatusOrData& other) {               \
222     if (this == &other) {                                                      \
223       return *this;                                                            \
224     }                                                                          \
225                                                                                \
226     if (other.ok()) {                                                          \
227       Assign(other.data_);                                                     \
228     } else {                                                                   \
229       AssignStatus(other.status_);                                             \
230     }                                                                          \
231     return *this;                                                              \
232   }                                                                            \
233                                                                                \
234   constexpr StatusOrData& operator=(StatusOrData&& other) {                    \
235     if (this == &other) {                                                      \
236       return *this;                                                            \
237     }                                                                          \
238                                                                                \
239     if (other.ok()) {                                                          \
240       Assign(std::move(other.data_));                                          \
241     } else {                                                                   \
242       AssignStatus(std::move(other.status_));                                  \
243     }                                                                          \
244     return *this;                                                              \
245   }                                                                            \
246                                                                                \
247   template <typename U>                                                        \
248   constexpr void Assign(U&& value) {                                           \
249     if (ok()) {                                                                \
250       data_ = std::forward<U>(value);                                          \
251     } else {                                                                   \
252       MakeValue(std::forward<U>(value));                                       \
253       status_ = OkStatus();                                                    \
254     }                                                                          \
255   }                                                                            \
256                                                                                \
257   template <typename U>                                                        \
258   constexpr void AssignStatus(U&& v) {                                         \
259     Clear();                                                                   \
260     status_ = static_cast<Status>(std::forward<U>(v));                         \
261     PW_ASSERT(!status_.ok());                                                  \
262   }                                                                            \
263                                                                                \
264   constexpr bool ok() const { return status_.ok(); }                           \
265                                                                                \
266  protected:                                                                    \
267   union {                                                                      \
268     Status status_;                                                            \
269   };                                                                           \
270                                                                                \
271   struct Empty {};                                                             \
272   union {                                                                      \
273     Empty unused_;                                                             \
274     T data_;                                                                   \
275   };                                                                           \
276                                                                                \
277   constexpr void Clear() {                                                     \
278     if (ok()) {                                                                \
279       data_.~T();                                                              \
280     }                                                                          \
281   }                                                                            \
282                                                                                \
283   template <typename... Arg>                                                   \
284   void MakeValue(Arg&&... arg) {                                               \
285     internal_result::PlacementNew<T>(&unused_, std::forward<Arg>(arg)...);     \
286   }                                                                            \
287   static_assert(true, "Macros must be terminated with a semicolon")
288 
289 template <typename T>
290 class StatusOrData<T, true> {
291   PW_RESULT_STATUS_OR_DATA_IMPL;
292 };
293 
294 template <typename T>
295 class StatusOrData<T, false> {
296   PW_RESULT_STATUS_OR_DATA_IMPL;
297 
298  public:
299   // Add a destructor since T is not trivially destructible.
300   ~StatusOrData() {
301     if (ok()) {
302       data_.~T();
303     }
304   }
305 };
306 
307 #undef PW_RESULT_STATUS_OR_DATA_IMPL
308 
309 PW_MODIFY_DIAGNOSTICS_POP();
310 
311 // Helper base classes to allow implicitly deleted constructors and assignment
312 // operators in `Result`. For example, `CopyCtorBase` will explicitly delete
313 // the copy constructor when T is not copy constructible and `Result` will
314 // inherit that behavior implicitly.
315 template <typename T, bool = std::is_copy_constructible<T>::value>
316 struct CopyCtorBase {
317   CopyCtorBase() = default;
318   CopyCtorBase(const CopyCtorBase&) = default;
319   CopyCtorBase(CopyCtorBase&&) = default;
320   CopyCtorBase& operator=(const CopyCtorBase&) = default;
321   CopyCtorBase& operator=(CopyCtorBase&&) = default;
322 };
323 
324 template <typename T>
325 struct CopyCtorBase<T, false> {
326   CopyCtorBase() = default;
327   CopyCtorBase(const CopyCtorBase&) = delete;
328   CopyCtorBase(CopyCtorBase&&) = default;
329   CopyCtorBase& operator=(const CopyCtorBase&) = default;
330   CopyCtorBase& operator=(CopyCtorBase&&) = default;
331 };
332 
333 template <typename T, bool = std::is_move_constructible<T>::value>
334 struct MoveCtorBase {
335   MoveCtorBase() = default;
336   MoveCtorBase(const MoveCtorBase&) = default;
337   MoveCtorBase(MoveCtorBase&&) = default;
338   MoveCtorBase& operator=(const MoveCtorBase&) = default;
339   MoveCtorBase& operator=(MoveCtorBase&&) = default;
340 };
341 
342 template <typename T>
343 struct MoveCtorBase<T, false> {
344   MoveCtorBase() = default;
345   MoveCtorBase(const MoveCtorBase&) = default;
346   MoveCtorBase(MoveCtorBase&&) = delete;
347   MoveCtorBase& operator=(const MoveCtorBase&) = default;
348   MoveCtorBase& operator=(MoveCtorBase&&) = default;
349 };
350 
351 template <typename T,
352           bool = std::is_copy_constructible<T>::value &&
353                  std::is_copy_assignable<T>::value>
354 struct CopyAssignBase {
355   CopyAssignBase() = default;
356   CopyAssignBase(const CopyAssignBase&) = default;
357   CopyAssignBase(CopyAssignBase&&) = default;
358   CopyAssignBase& operator=(const CopyAssignBase&) = default;
359   CopyAssignBase& operator=(CopyAssignBase&&) = default;
360 };
361 
362 template <typename T>
363 struct CopyAssignBase<T, false> {
364   CopyAssignBase() = default;
365   CopyAssignBase(const CopyAssignBase&) = default;
366   CopyAssignBase(CopyAssignBase&&) = default;
367   CopyAssignBase& operator=(const CopyAssignBase&) = delete;
368   CopyAssignBase& operator=(CopyAssignBase&&) = default;
369 };
370 
371 template <typename T,
372           bool = std::is_move_constructible<T>::value &&
373                  std::is_move_assignable<T>::value>
374 struct MoveAssignBase {
375   MoveAssignBase() = default;
376   MoveAssignBase(const MoveAssignBase&) = default;
377   MoveAssignBase(MoveAssignBase&&) = default;
378   MoveAssignBase& operator=(const MoveAssignBase&) = default;
379   MoveAssignBase& operator=(MoveAssignBase&&) = default;
380 };
381 
382 template <typename T>
383 struct MoveAssignBase<T, false> {
384   MoveAssignBase() = default;
385   MoveAssignBase(const MoveAssignBase&) = default;
386   MoveAssignBase(MoveAssignBase&&) = default;
387   MoveAssignBase& operator=(const MoveAssignBase&) = default;
388   MoveAssignBase& operator=(MoveAssignBase&&) = delete;
389 };
390 
391 }  // namespace internal_result
392 }  // namespace pw
393