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:
parent
813bfbd061
commit
df72914a60
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue