Commit 1eafe5a0 authored by liberato@chromium.org's avatar liberato@chromium.org Committed by Commit Bot

Added ErrorOr<> for MediaError returns.

ErrorOr<> makes it easier to return an arbitrary value or an error.
For example, one might:

ErrorOr<std::unique_ptr<int>> Foo() {
  // return a ptr, or a MediaError explaining why not.
}

Change-Id: I819b29c1dcc9ba2512ab709da2ae6b267a24d8b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2040374
Commit-Queue: Frank Liberato <liberato@chromium.org>
Reviewed-by: default avatarTed Meyer <tmathmeyer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#739144}
parent 35bf0120
......@@ -137,6 +137,68 @@ class MEDIA_EXPORT MediaError {
#define MEDIA_ERROR_INTERNAL(...) ::media::MediaError(__VA_ARGS__)
// Helper class to allow returning a |T| or a MediaError. Typical usage:
//
// ErrorOr<std::unique_ptr<MyObject>> FactoryFn() {
// if (success)
// return std::make_unique<MyObject>();
// return MediaError(ErrorCodes::kSomethingBadHappened);
// }
//
// auto result = FactoryFn();
// if (result.has_error()) return std::move(result.error());
// my_object_ = std::move(result.value());
//
// Also useful if one would like to get an enum class return value, unless an
// error occurs:
//
// enum class ResultType { kNeedMoreInput, kOutputIsReady, kFormatChanged };
//
// ErrorOr<ResultType> Foo() { ... }
//
// auto result = Foo();
// if (result.has_error()) return std::move(result.error());
// switch (result.value()) {
// case ResultType::kNeedMoreInput:
// ...
// }
template <typename T>
class ErrorOr {
public:
// All of these may be implicit, so that one may just return MediaError or
// the value in question.
ErrorOr(MediaError&& error) : error_(std::move(error)) {}
ErrorOr(const MediaError& error) : error_(error) {}
ErrorOr(T&& value) : value_(std::move(value)) {}
ErrorOr(const T& value) : value_(value) {}
~ErrorOr() = default;
// Move- and copy- construction and assignment are okay.
ErrorOr(const ErrorOr&) = default;
ErrorOr(ErrorOr&&) = default;
ErrorOr& operator=(ErrorOr&) = default;
ErrorOr& operator=(ErrorOr&&) = default;
// Do we have a value?
bool has_value() const { return value_.has_value(); }
// Since we often test for errors, provide this too.
bool has_error() const { return !has_value(); }
// Return the error, if we have one. Up to the caller to make sure that we
// have one via |!has_value()|.
MediaError& error() { return *error_; }
// Return a ref to the value. It's up to the caller to verify that we have a
// value before calling this.
T& value() { return *value_; }
private:
base::Optional<MediaError> error_;
base::Optional<T> value_;
};
} // namespace media
#endif // MEDIA_BASE_MEDIA_ERROR_H_
......@@ -7,6 +7,8 @@
namespace media {
// NOTE: These numbers are still subject to change. Do not use for things like
// UMA yet!
enum class ErrorCode : uint32_t {
kOk = 0,
kCodeOnlyForTesting = std::numeric_limits<uint32_t>::max(),
......
......@@ -64,6 +64,13 @@ class MediaErrorTest : public testing::Test {
me.WithData("data", "Hey you! psst! Help me outta here! I'm trapped!");
return me;
}
// Make sure that the typical usage of ErrorOr actually compiles.
ErrorOr<std::unique_ptr<int>> TypicalErrorOrUsage(bool succeed) {
if (succeed)
return std::make_unique<int>(123);
return MediaError(ErrorCode::kCodeOnlyForTesting);
}
};
TEST_F(MediaErrorTest, StaticOKMethodGivesCorrectSerialization) {
......@@ -185,4 +192,55 @@ TEST_F(MediaErrorTest, CanCopyEasily) {
ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul);
}
TEST_F(MediaErrorTest, ErrorOrTypicalUsage) {
// Mostly so we have some code coverage on the default usage.
EXPECT_TRUE(TypicalErrorOrUsage(true).has_value());
EXPECT_FALSE(TypicalErrorOrUsage(true).has_error());
EXPECT_FALSE(TypicalErrorOrUsage(false).has_value());
EXPECT_TRUE(TypicalErrorOrUsage(false).has_error());
}
TEST_F(MediaErrorTest, ErrorOrWithMoveOnlyType) {
ErrorOr<std::unique_ptr<int>> error_or(std::make_unique<int>(123));
EXPECT_TRUE(error_or.has_value());
EXPECT_FALSE(error_or.has_error());
std::unique_ptr<int> result = std::move(error_or.value());
EXPECT_EQ(error_or.value(), nullptr);
EXPECT_NE(result.get(), nullptr);
EXPECT_EQ(*result, 123);
}
TEST_F(MediaErrorTest, ErrorOrWithCopyableType) {
ErrorOr<int> error_or(123);
EXPECT_TRUE(error_or.has_value());
EXPECT_FALSE(error_or.has_error());
int result = std::move(error_or.value());
EXPECT_EQ(result, 123);
// Should be unaffected by the move.
EXPECT_EQ(error_or.value(), 123);
}
TEST_F(MediaErrorTest, ErrorOrMoveConstructionAndAssignment) {
// Make sure that we can move-construct and move-assign a move-only value.
ErrorOr<std::unique_ptr<int>> error_or_0(std::make_unique<int>(123));
ErrorOr<std::unique_ptr<int>> error_or_1(std::move(error_or_0));
EXPECT_EQ(error_or_0.value(), nullptr);
ErrorOr<std::unique_ptr<int>> error_or_2 = std::move(error_or_1);
EXPECT_EQ(error_or_1.value(), nullptr);
// |error_or_2| should have gotten the original.
std::unique_ptr<int> value = std::move(error_or_2.value());
EXPECT_EQ(*value, 123);
}
TEST_F(MediaErrorTest, ErrorOrCopyWorks) {
// Make sure that we can move-construct and move-assign a move-only value.
ErrorOr<int> error_or_0(123);
ErrorOr<int> error_or_1(std::move(error_or_0));
ErrorOr<int> error_or_2 = std::move(error_or_1);
EXPECT_EQ(error_or_2.value(), 123);
}
} // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment