Result: Add default template for Result<T, E>

It currently is a tagged pair instead of a tagged union because it was
much easier to write and the optimization can come later.

BUG=dawn:19

Change-Id: Idbfd86d559655b38871c2d1768bdd758e797dfbd
Reviewed-on: https://dawn-review.googlesource.com/c/2701
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
Corentin Wallez 2018-11-29 12:40:26 +00:00 committed by Commit Bot service account
parent 813bfbd061
commit df72914a60
2 changed files with 137 additions and 0 deletions

View File

@ -20,6 +20,7 @@
#include <cstddef>
#include <cstdint>
#include <utility>
// Result<T, E> is the following sum type (Haskell notation):
//
@ -124,6 +125,38 @@ class DAWN_NO_DISCARD Result<T*, E*> {
intptr_t mPayload = kEmptyPayload;
};
// Catchall definition of Result<T, E> implemented as a tagged struct. It could be improved to use
// a tagged union instead if it turns out to be a hotspot. T and E must be movable and default
// constructible.
template <typename T, typename E>
class DAWN_NO_DISCARD Result {
public:
Result(T&& success);
Result(E&& error);
Result(Result<T, E>&& other);
Result<T, E>& operator=(Result<T, E>&& other);
~Result();
bool IsError() const;
bool IsSuccess() const;
T&& AcquireSuccess();
E&& AcquireError();
private:
enum PayloadType {
Success = 0,
Error = 1,
Acquired = 2,
};
PayloadType mType;
E mError;
T mSuccess;
};
// Implementation of Result<void, E*>
template <typename E>
Result<void, E*>::Result() {
@ -247,4 +280,56 @@ E* Result<T*, E*>::GetErrorFromPayload(intptr_t payload) {
return reinterpret_cast<E*>(payload ^ 1);
}
// Implementation of Result<T, E>
template <typename T, typename E>
Result<T, E>::Result(T&& success) : mType(Success), mSuccess(success) {
}
template <typename T, typename E>
Result<T, E>::Result(E&& error) : mType(Error), mError(error) {
}
template <typename T, typename E>
Result<T, E>::~Result() {
ASSERT(mType == Acquired);
}
template <typename T, typename E>
Result<T, E>::Result(Result<T, E>&& other)
: mType(other.mType), mError(std::move(other.mError)), mSuccess(other.mSuccess) {
other.mType = Acquired;
}
template <typename T, typename E>
Result<T, E>& Result<T, E>::operator=(Result<T, E>&& other) {
mType = other.mType;
mError = std::move(other.mError);
mSuccess = std::move(other.mSuccess);
other.mType = Acquired;
return *this;
}
template <typename T, typename E>
bool Result<T, E>::IsError() const {
return mType == Error;
}
template <typename T, typename E>
bool Result<T, E>::IsSuccess() const {
return mType == Success;
}
template <typename T, typename E>
T&& Result<T, E>::AcquireSuccess() {
ASSERT(mType == Success);
mType = Acquired;
return std::move(mSuccess);
}
template <typename T, typename E>
E&& Result<T, E>::AcquireError() {
ASSERT(mType == Error);
mType = Acquired;
return std::move(mError);
}
#endif // COMMON_RESULT_H_

View File

@ -39,6 +39,8 @@ void TestSuccess(Result<T, E>* result, T expectedSuccess) {
static int dummyError = 0xbeef;
static float dummySuccess = 42.0f;
// Result<void, E*>
// Test constructing an error Result<void, E*>
TEST(ResultOnlyPointerError, ConstructingError) {
Result<void, int*> result(&dummyError);
@ -88,6 +90,8 @@ TEST(ResultOnlyPointerError, ReturningSuccess) {
ASSERT_FALSE(result.IsError());
}
// Result<T*, E*>
// Test constructing an error Result<T*, E*>
TEST(ResultBothPointer, ConstructingError) {
Result<float*, int*> result(&dummyError);
@ -134,4 +138,52 @@ TEST(ResultBothPointer, ReturningSuccess) {
TestSuccess(&result, &dummySuccess);
}
// Result<T, E>
// Test constructing an error Result<T, E>
TEST(ResultGeneric, ConstructingError) {
Result<std::vector<float>, int*> result(&dummyError);
TestError(&result, &dummyError);
}
// Test moving an error Result<T, E>
TEST(ResultGeneric, MovingError) {
Result<std::vector<float>, int*> result(&dummyError);
Result<std::vector<float>, int*> movedResult(std::move(result));
TestError(&movedResult, &dummyError);
}
// Test returning an error Result<T, E>
TEST(ResultGeneric, ReturningError) {
auto CreateError = []() -> Result<std::vector<float>, int*> {
return {&dummyError};
};
Result<std::vector<float>, int*> result = CreateError();
TestError(&result, &dummyError);
}
// Test constructing a success Result<T, E>
TEST(ResultGeneric, ConstructingSuccess) {
Result<std::vector<float>, int*> result({1.0f});
TestSuccess(&result, {1.0f});
}
// Test moving a success Result<T, E>
TEST(ResultGeneric, MovingSuccess) {
Result<std::vector<float>, int*> result({1.0f});
Result<std::vector<float>, int*> movedResult(std::move(result));
TestSuccess(&movedResult, {1.0f});
}
// Test returning a success Result<T, E>
TEST(ResultGeneric, ReturningSuccess) {
auto CreateSuccess = []() -> Result<std::vector<float>, int*> {
return {{1.0f}};
};
Result<std::vector<float>, int*> result = CreateSuccess();
TestSuccess(&result, {1.0f});
}
} // anonymous namespace