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