9f5f71616a
Additionally, add tests for the macros. A future CL will enable the tests in CI. Change-Id: Id4445a1aa65bf6751b87606f37654f3fc6d20efc Tested-By: kanepyork <rikingcoding@gmail.com> Reviewed-on: https://cl.tvl.fyi/c/depot/+/1274 Reviewed-by: tazjin <mail@tazj.in> Reviewed-by: glittershark <grfn@gws.fyi> Tested-by: BuildkiteCI
250 lines
6.4 KiB
C++
250 lines
6.4 KiB
C++
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==============================================================================*/
|
|
|
|
#ifndef ABSL_STATUS_STATUSOR_INTERNALS_H_
|
|
#define ABSL_STATUS_STATUSOR_INTERNALS_H_
|
|
|
|
#include "absl/status/status.h"
|
|
|
|
namespace absl {
|
|
ABSL_NAMESPACE_BEGIN
|
|
|
|
namespace internal_statusor {
|
|
|
|
class Helper {
|
|
public:
|
|
// Move type-agnostic error handling to the .cc.
|
|
static void HandleInvalidStatusCtorArg(Status*);
|
|
ABSL_ATTRIBUTE_NORETURN 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(other.status_);
|
|
}
|
|
}
|
|
|
|
template <typename U>
|
|
StatusOrData(const StatusOrData<U>& other) {
|
|
if (other.ok()) {
|
|
MakeValue(other.data_);
|
|
MakeStatus();
|
|
} else {
|
|
MakeStatus(other.status_);
|
|
}
|
|
}
|
|
|
|
template <typename U>
|
|
StatusOrData(StatusOrData<U>&& other) {
|
|
if (other.ok()) {
|
|
MakeValue(std::move(other.data_));
|
|
MakeStatus();
|
|
} else {
|
|
MakeStatus(other.status_);
|
|
}
|
|
}
|
|
|
|
explicit StatusOrData(const T& value) : data_(value) { MakeStatus(); }
|
|
explicit StatusOrData(T&& value) : data_(std::move(value)) { MakeStatus(); }
|
|
|
|
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) {
|
|
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_ = OkStatus();
|
|
}
|
|
}
|
|
|
|
void Assign(T&& value) {
|
|
if (ok()) {
|
|
data_.~T();
|
|
MakeValue(std::move(value));
|
|
} else {
|
|
MakeValue(std::move(value));
|
|
status_ = OkStatus();
|
|
}
|
|
}
|
|
|
|
void Assign(const Status& status) {
|
|
Clear();
|
|
status_ = status;
|
|
EnsureNotOk();
|
|
}
|
|
|
|
void Assign(Status&& status) {
|
|
Clear();
|
|
// Note that we copy instead of moving the status here so that
|
|
// status.~StatusOrData() can call ok() without invoking UB.
|
|
status_ = status;
|
|
EnsureNotOk();
|
|
}
|
|
|
|
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 {
|
|
Status status_;
|
|
};
|
|
|
|
// data_ is active iff status_.ok()==true
|
|
struct Dummy {};
|
|
union {
|
|
// 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&&) = default;
|
|
TraitsBase& operator=(const TraitsBase&) = default;
|
|
TraitsBase& operator=(TraitsBase&&) = 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 internal_statusor
|
|
|
|
ABSL_NAMESPACE_END
|
|
} // namespace absl
|
|
|
|
#endif // ABSL_STATUS_STATUSOR_INTERNALS_H_
|