Merge pull request #1455 from yuriks/ResultVal-union

core: Use unrestricted union to hold storage of ResultVal value
This commit is contained in:
Mathew Maidment 2016-03-05 23:29:14 -05:00
commit 8ee230fe1c

View file

@ -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));
}
}; };
/** /**