| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COBALT_SRC_PUBLIC_LIB_STATUSOR_STATUSOR_INTERNALS_H_ |
| #define COBALT_SRC_PUBLIC_LIB_STATUSOR_STATUSOR_INTERNALS_H_ |
| |
| #include <utility> |
| |
| #include "src/public/lib/status.h" |
| |
| namespace cobalt::lib::statusor::internal_statusor { |
| |
| class Helper { |
| public: |
| // Move type-agnostic error handling to the .cc. |
| static void HandleInvalidStatusCtorArg(Status* /*status*/); |
| static void Crash(const Status& status); |
| }; |
| |
| // Construct an instance of T in `p` through placement new, passing Args... to |
| // the constructor. |
| // This abstraction is here mostly for the gcc performance fix. |
| template <typename T, typename... Args> |
| void PlacementNew(void* p, Args&&... args) { |
| #if defined(__GNUC__) && !defined(__clang__) |
| // Teach gcc that 'p' cannot be null, fixing code size issues. |
| if (p == nullptr) |
| __builtin_unreachable(); |
| #endif |
| new (p) T(std::forward<Args>(args)...); |
| } |
| |
| // Helper base class to hold the data and all operations. |
| // We move all this to a base class to allow mixing with the appropriate |
| // TraitsBase specialization. |
| template <typename T> |
| class StatusOrData { |
| template <typename U> |
| friend class StatusOrData; |
| |
| public: |
| StatusOrData() = delete; |
| |
| StatusOrData(const StatusOrData& other) { |
| if (other.ok()) { |
| MakeValue(other.data_); |
| MakeStatus(); |
| } else { |
| MakeStatus(other.status_); |
| } |
| } |
| |
| StatusOrData(StatusOrData&& other) noexcept { |
| if (other.ok()) { |
| MakeValue(std::move(other.data_)); |
| MakeStatus(); |
| } else { |
| MakeStatus(std::move(other.status_)); |
| } |
| } |
| |
| template <typename U> |
| StatusOrData(const StatusOrData<U>& other) { // NOLINT(google-explicit-constructor) |
| if (other.ok()) { |
| MakeValue(other.data_); |
| MakeStatus(); |
| } else { |
| MakeStatus(other.status_); |
| } |
| } |
| |
| template <typename U> |
| StatusOrData(StatusOrData<U>&& other) { // NOLINT(google-explicit-constructor) |
| if (other.ok()) { |
| MakeValue(std::move(other.data_)); |
| MakeStatus(); |
| } else { |
| MakeStatus(std::move(other.status_)); |
| } |
| } |
| |
| // NOLINTNEXTLINE(modernize-pass-by-value) |
| explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); } |
| explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); } |
| |
| // NOLINTNEXTLINE(modernize-pass-by-value) |
| explicit StatusOrData(const Status& status) : status_(status) { EnsureNotOk(); } |
| explicit StatusOrData(Status&& status) : status_(std::move(status)) { EnsureNotOk(); } |
| |
| StatusOrData& operator=(const StatusOrData& other) { |
| if (this == &other) { |
| return *this; |
| } |
| |
| if (other.ok()) { |
| Assign(other.data_); |
| } else { |
| Assign(other.status_); |
| } |
| return *this; |
| } |
| |
| StatusOrData& operator=(StatusOrData&& other) noexcept { |
| if (this == &other) { |
| return *this; |
| } |
| |
| if (other.ok()) { |
| Assign(std::move(other.data_)); |
| } else { |
| Assign(std::move(other.status_)); |
| } |
| return *this; |
| } |
| |
| ~StatusOrData() { |
| if (ok()) { |
| status_.~Status(); |
| data_.~T(); |
| } else { |
| status_.~Status(); |
| } |
| } |
| |
| void Assign(const T& value) { |
| if (ok()) { |
| data_.~T(); |
| MakeValue(value); |
| } else { |
| MakeValue(value); |
| status_ = Status::OkStatus(); |
| } |
| } |
| |
| void Assign(T&& value) { |
| if (ok()) { |
| data_.~T(); |
| MakeValue(std::move(value)); |
| } else { |
| MakeValue(std::move(value)); |
| status_ = Status::OkStatus(); |
| } |
| } |
| |
| void Assign(const Status& status) { |
| Clear(); |
| status_ = status; |
| EnsureNotOk(); |
| } |
| |
| void Assign(Status&& status) { |
| Clear(); |
| status_ = std::move(status); |
| EnsureNotOk(); |
| } |
| |
| [[nodiscard]] bool ok() const { return status_.ok(); } |
| |
| protected: |
| // status_ will always be active after the constructor. |
| // We make it a union to be able to initialize exactly how we need without |
| // waste. |
| // Eg. in the copy constructor we use the default constructor of Status in |
| // the ok() path to avoid an extra Ref call. |
| union { // NOLINT(misc-non-private-member-variables-in-classes) |
| Status status_; |
| }; |
| |
| // data_ is active iff status_.ok()==true |
| struct Dummy {}; |
| union { // NOLINT(misc-non-private-member-variables-in-classes) |
| // When T is const, we need some non-const object we can cast to void* for |
| // the placement new. dummy_ is that object. |
| Dummy dummy_; |
| T data_; |
| }; |
| |
| void Clear() { |
| if (ok()) { |
| data_.~T(); |
| } |
| } |
| |
| void EnsureOk() const { |
| if (!ok()) { |
| Helper::Crash(status_); |
| } |
| } |
| |
| void EnsureNotOk() { |
| if (ok()) { |
| Helper::HandleInvalidStatusCtorArg(&status_); |
| } |
| } |
| |
| // Construct the value (ie. data_) through placement new with the passed |
| // argument. |
| template <typename Arg> |
| void MakeValue(Arg&& arg) { |
| internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)); |
| } |
| |
| // Construct the status (ie. status_) through placement new with the passed |
| // argument. |
| template <typename... Args> |
| void MakeStatus(Args&&... args) { |
| internal_statusor::PlacementNew<Status>(&status_, std::forward<Args>(args)...); |
| } |
| }; |
| |
| // Helper base class to allow implicitly deleted constructors and assignment |
| // operations in StatusOr. |
| // TraitsBase will explicitly delete what it can't support and StatusOr will |
| // inherit that behavior implicitly. |
| template <bool Copy, bool Move> |
| struct TraitsBase { |
| TraitsBase() = default; |
| TraitsBase(const TraitsBase&) = default; |
| TraitsBase(TraitsBase&&) noexcept = default; |
| TraitsBase& operator=(const TraitsBase&) = default; |
| TraitsBase& operator=(TraitsBase&&) noexcept = default; |
| }; |
| |
| template <> |
| struct TraitsBase<false, true> { |
| TraitsBase() = default; |
| TraitsBase(const TraitsBase&) = delete; |
| TraitsBase(TraitsBase&&) = default; |
| TraitsBase& operator=(const TraitsBase&) = delete; |
| TraitsBase& operator=(TraitsBase&&) = default; |
| }; |
| |
| template <> |
| struct TraitsBase<false, false> { |
| TraitsBase() = default; |
| TraitsBase(const TraitsBase&) = delete; |
| TraitsBase(TraitsBase&&) = delete; |
| TraitsBase& operator=(const TraitsBase&) = delete; |
| TraitsBase& operator=(TraitsBase&&) = delete; |
| }; |
| |
| } // namespace cobalt::lib::statusor::internal_statusor |
| |
| #endif // COBALT_SRC_PUBLIC_LIB_STATUSOR_STATUSOR_INTERNALS_H_ |