blob: ed8444947a333b6c416fc0963fdb4475a749fd1d [file] [log] [blame]
// 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_