Merge pull request #1455 from yuriks/ResultVal-union
core: Use unrestricted union to hold storage of ResultVal value
This commit is contained in:
commit
8ee230fe1c
|
@ -269,7 +269,6 @@ public:
|
||||||
: result_code(error_code)
|
: result_code(error_code)
|
||||||
{
|
{
|
||||||
ASSERT(error_code.IsError());
|
ASSERT(error_code.IsError());
|
||||||
UpdateDebugPtr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,40 +286,37 @@ public:
|
||||||
: result_code(o.result_code)
|
: result_code(o.result_code)
|
||||||
{
|
{
|
||||||
if (!o.empty()) {
|
if (!o.empty()) {
|
||||||
new (&storage) T(*o.GetPointer());
|
new (&object) T(o.object);
|
||||||
}
|
}
|
||||||
UpdateDebugPtr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal(ResultVal&& o)
|
ResultVal(ResultVal&& o)
|
||||||
: result_code(o.result_code)
|
: result_code(o.result_code)
|
||||||
{
|
{
|
||||||
if (!o.empty()) {
|
if (!o.empty()) {
|
||||||
new (&storage) T(std::move(*o.GetPointer()));
|
new (&object) T(std::move(o.object));
|
||||||
}
|
}
|
||||||
UpdateDebugPtr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~ResultVal() {
|
~ResultVal() {
|
||||||
if (!empty()) {
|
if (!empty()) {
|
||||||
GetPointer()->~T();
|
object.~T();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal& operator=(const ResultVal& o) {
|
ResultVal& operator=(const ResultVal& o) {
|
||||||
if (!empty()) {
|
if (!empty()) {
|
||||||
if (!o.empty()) {
|
if (!o.empty()) {
|
||||||
*GetPointer() = *o.GetPointer();
|
object = o.object;
|
||||||
} else {
|
} else {
|
||||||
GetPointer()->~T();
|
object.~T();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!o.empty()) {
|
if (!o.empty()) {
|
||||||
new (&storage) T(*o.GetPointer());
|
new (&object) T(o.object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result_code = o.result_code;
|
result_code = o.result_code;
|
||||||
UpdateDebugPtr();
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -333,11 +329,10 @@ public:
|
||||||
void emplace(ResultCode success_code, Args&&... args) {
|
void emplace(ResultCode success_code, Args&&... args) {
|
||||||
ASSERT(success_code.IsSuccess());
|
ASSERT(success_code.IsSuccess());
|
||||||
if (!empty()) {
|
if (!empty()) {
|
||||||
GetPointer()->~T();
|
object.~T();
|
||||||
}
|
}
|
||||||
new (&storage) T(std::forward<Args>(args)...);
|
new (&object) T(std::forward<Args>(args)...);
|
||||||
result_code = success_code;
|
result_code = success_code;
|
||||||
UpdateDebugPtr();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the `ResultVal` contains an error code and no value.
|
/// Returns true if the `ResultVal` contains an error code and no value.
|
||||||
|
@ -350,15 +345,15 @@ public:
|
||||||
|
|
||||||
ResultCode Code() const { return result_code; }
|
ResultCode Code() const { return result_code; }
|
||||||
|
|
||||||
const T& operator* () const { return *GetPointer(); }
|
const T& operator* () const { return object; }
|
||||||
T& operator* () { return *GetPointer(); }
|
T& operator* () { return object; }
|
||||||
const T* operator->() const { return GetPointer(); }
|
const T* operator->() const { return &object; }
|
||||||
T* operator->() { return GetPointer(); }
|
T* operator->() { return &object; }
|
||||||
|
|
||||||
/// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
|
/// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
|
||||||
template <typename U>
|
template <typename U>
|
||||||
T ValueOr(U&& value) const {
|
T ValueOr(U&& value) const {
|
||||||
return !empty() ? *GetPointer() : std::move(value);
|
return !empty() ? object : std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that the result succeeded and returns a reference to it.
|
/// Asserts that the result succeeded and returns a reference to it.
|
||||||
|
@ -372,31 +367,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
|
// A union is used to allocate the storage for the value, while allowing us to construct and
|
||||||
|
// destruct it at will.
|
||||||
StorageType storage;
|
union { T object; };
|
||||||
ResultCode result_code;
|
ResultCode result_code;
|
||||||
#ifdef _DEBUG
|
|
||||||
// The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
|
|
||||||
// need to cast `storage` to a pointer or pay attention to `result_code`.
|
|
||||||
const T* debug_ptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void UpdateDebugPtr() {
|
|
||||||
#ifdef _DEBUG
|
|
||||||
debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* GetPointer() const {
|
|
||||||
ASSERT(!empty());
|
|
||||||
return static_cast<const T*>(static_cast<const void*>(&storage));
|
|
||||||
}
|
|
||||||
|
|
||||||
T* GetPointer() {
|
|
||||||
ASSERT(!empty());
|
|
||||||
return static_cast<T*>(static_cast<void*>(&storage));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Reference in New Issue