Add Result<Success, Error> type.
Result<Success, Error> is a tagged union that represents the success or
failure of an operation, along with either the success value or error
details.
Bug: 679313
Change-Id: Ic815cf1af0ad7f283460494db241b03957546f6b
Reviewed-on: https://chromium-review.googlesource.com/c/1393668
Commit-Queue: Erik Jensen <rkjnsn@chromium.org>
Reviewed-by: Lambros Lambrou <lambroslambrou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#623778}
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn
index b43c596..e362c45 100644
--- a/remoting/base/BUILD.gn
+++ b/remoting/base/BUILD.gn
@@ -29,6 +29,7 @@
"rate_counter.h",
"remoting_bot.cc",
"remoting_bot.h",
+ "result.h",
"rsa_key_pair.cc",
"rsa_key_pair.h",
"running_samples.cc",
@@ -168,6 +169,7 @@
"compound_buffer_unittest.cc",
"oauth_helper_unittest.cc",
"rate_counter_unittest.cc",
+ "result_unittest.cc",
"rsa_key_pair_unittest.cc",
"run_all_unittests.cc",
"running_samples_unittest.cc",
diff --git a/remoting/base/result.h b/remoting/base/result.h
new file mode 100644
index 0000000..ee272eda
--- /dev/null
+++ b/remoting/base/result.h
@@ -0,0 +1,670 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_BASE_RESULT_H_
+#define REMOTING_BASE_RESULT_H_
+
+#include <type_traits>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/optional.h"
+
+// Result<SuccessType, ErrorType> represents the success or failure of an
+// operation, along with either the success value or error details.
+//
+// It can be convenient to alias Result for specific error types. For example,
+// template <SuccessType>
+// using PosixResult<SuccessType, int>
+//
+// PosixResult<size_t> MyRead(int fd, void* buf, size_t count);
+
+// Synopsis:
+//
+// SuccessType
+// ErrorType
+// The success and error types of the Result.
+//
+// Result()
+// Only present when the success value is default constructible. Default
+// constructs the success value. This is useful for situations like IPC
+// deserialization where a default-costructed instance is created and the
+// actual value is filled in later. In general, prefer using the
+// Result(kSuccessTag) constructor to be explicit.
+//
+// Result(SuccessTag, Args&&... args)
+// Result(ErrorTag, Args&&... args)
+// Direct constructs the success or error value, respectively, with the
+// provided arguments.
+//
+// Result(T&& success_value)
+// Result(T&& error_value)
+// Implicitly constructs either the success or error value. Only callable if
+// T is implicitly convertible to SuccessType or ErrorType but not both.
+//
+// Result(const Result& other)
+// Result(Result&& other)
+// Copy / move constructors. Only present if both SuccessType and ErrorType
+// are copyable (for the first overload) / moveable (for the second).
+//
+// Result(const Result<S, E>& other)
+// Result(Result<S, E>&& other)
+// Conversion constructors from a compatible Result. Only callable if S is
+// copy (for the first overload) / move (for the second) convertible to
+// SuccessType and E is convertible to ErrorType.
+//
+// Result& operator=(const Result& other)
+// Result& operator=(Result&& other)
+// Copy / move assignment. If this!=*other, destroys the current value and
+// copy / move constructs the current value from other. Only present if both
+// SuccessType and ErrorType are copy (for the first overload) / move (for
+// the second) constructible.
+//
+// Result& operator=(const Result<S, E>& other)
+// Result& operator=(Result<S, E>&& other)
+// Conversion assignment. Only callable if S is copy (for the first overload)
+// / move (for the second) convertible to SuccessType and E is copy / move
+// convertible to ErrorType.
+//
+// EmplaceSuccess(Args&&... args)
+// EmplaceError(Args&&... args)
+// Destroys the current value and direct constructs the success or error value
+// with the provided arguments.
+//
+// Result<NewSuccessType, ErrorType> Map(F&& on_success) const&
+// Result<NewSuccessType, ErrorType> Map(F&& on_success) &&
+// If is_success(), calls on_success passing the current value, and returns a
+// new Result using the return value. If is_error(), the error value is passed
+// through to the new result unchanged.
+//
+// Result<SuccessType, NewErrorType> MapError(F&& on_error) const&
+// Result<SuccessType, NewErrorType> MapError(F&& on_error) &&
+// If is_error(), calls on_error passing the current value, and returns a new
+// Result using the return value. If is_success(), the success value is passed
+// through to the new result unchanged.
+//
+// Result<NewSuccessType, ErrorType> AndThen(F&& on_success) const&
+// Result<NewSuccessType, ErrorType> AndThen(F&& on_success) &&
+// If is_success(), calls on_success passing the current value, which should
+// itself return a Result. That Result is then returned, converting an error
+// to ErrorType if necessary. If is_error(), the error value is passed through
+// to the new result unchanged.
+//
+// Result<NewSuccessType, ErrorType> OrElse(F&& on_error) const&
+// Result<NewSuccessType, ErrorType> OrElse(F&& on_error) &&
+// If is_error(), calls on_error passing the current value, which should
+// itself return a Result. That Result is then returned, converting a success
+// value to SuccessType if necessary. If is_success(), the success value is
+// passed through to the new result unchanged.
+//
+// R Visit(F&& visitor) const&
+// R Visit(F&& visitor) &
+// R Visit(F&& visitor) &&
+// Calls either success() or error() on the provided visitor depending on the
+// state of the result, passing the value of the corresponding state and
+// returning the value returned by the visitor. success() and error() must
+// return the same type for the overload called. That is
+// success(const SuccessType&) must have the same return type as
+// error(const ErrorType&), but need not be the same as success(SuccessType&)
+// and error(ErrorType&) (if present).
+//
+// bool is_success() const
+// bool is_error() const
+// Check whether the Result currently holds a success value or an error.
+//
+// SuccessType& success()
+// const SuccessType& success() const
+// Retrieve the success value. Undefined behavior if !is_success().
+//
+// ErrorType& error()
+// const ErrorType& error() const
+// Retrieve the error value. Undefined behavior if !is_error().
+
+namespace remoting {
+
+// SuccessTag and ErrorTag are used for constructing a Result in the success
+// state or error state, respectively.
+class SuccessTag {};
+class ErrorTag {};
+// Monostate can be used for SuccessType or ErrorType to indicate that there is
+// no data for that state. Thus, Result<SomeType, Monostate> is somewhat
+// analogous to base::Optional<SomeType>, and Result<Monostate, Monostate> is
+// effectively a (2-byte) boolean. Result<Monostate, ErrorType> can be useful
+// for cases where an operation can fail, but there is no return value in the
+// success case.
+// TODO(rkjnsn): Replace with std::monostate once C++17 is allowed.
+class Monostate {};
+
+constexpr SuccessTag kSuccessTag = SuccessTag();
+constexpr ErrorTag kErrorTag = ErrorTag();
+constexpr Monostate kMonostate = Monostate();
+
+namespace internal {
+
+template <typename SuccessType, typename ErrorType>
+struct ResultStorage {
+ // Default constructor.
+ ResultStorage() : ResultStorage(kSuccessTag) {}
+
+ // Direct constructors.
+ template <typename... Args>
+ ResultStorage(SuccessTag, Args&&... args) : is_success(true) {
+ new (&success) SuccessType(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ ResultStorage(ErrorTag, Args&&... args) : is_success(false) {
+ new (&error) ErrorType(std::forward<Args>(args)...);
+ }
+
+ // Copy/move constructors.
+ ResultStorage(const ResultStorage& other) : is_success(other.is_success) {
+ if (is_success) {
+ new (&success) SuccessType(other.success);
+ } else {
+ new (&error) ErrorType(other.error);
+ }
+ }
+
+ ResultStorage(ResultStorage&& other) : is_success(other.is_success) {
+ if (is_success) {
+ new (&success) SuccessType(std::move(other.success));
+ } else {
+ new (&error) ErrorType(std::move(other.error));
+ }
+ }
+
+ // Conversion constructors.
+ template <typename S, typename E>
+ ResultStorage(const ResultStorage<S, E>& other)
+ : is_success(other.is_success) {
+ if (is_success) {
+ new (&success) SuccessType(other.success);
+ } else {
+ new (&error) ErrorType(other.error);
+ }
+ }
+
+ template <typename S, typename E>
+ ResultStorage(ResultStorage<S, E>&& other) : is_success(other.is_success) {
+ if (is_success) {
+ new (&success) SuccessType(std::move(other.success));
+ } else {
+ new (&error) ErrorType(std::move(other.error));
+ }
+ }
+
+ ~ResultStorage() {
+ if (is_success) {
+ success.~SuccessType();
+ } else {
+ error.~ErrorType();
+ }
+ }
+
+ // Assignment.
+ ResultStorage& operator=(const ResultStorage& other) {
+ if (this == &other) {
+ return *this;
+ }
+ this->~ResultStorage();
+ new (this) ResultStorage(other);
+ return *this;
+ }
+
+ ResultStorage& operator=(ResultStorage&& other) {
+ if (this == &other) {
+ return *this;
+ }
+ this->~ResultStorage();
+ new (this) ResultStorage(std::move(other));
+ return *this;
+ }
+
+ union {
+ SuccessType success;
+ ErrorType error;
+ };
+
+ bool is_success;
+};
+
+// The following structs are helpers to implement constructor/assign-operator
+// overloading. Result defines all five as "default", and then inherits from
+// these base classes so the compiler will include or omit each constructor or
+// operator as appropriate.
+template <bool is_default_constructible>
+struct DefaultConstructible {
+ constexpr DefaultConstructible() = default;
+ constexpr DefaultConstructible(int) {}
+};
+
+template <>
+struct DefaultConstructible<false> {
+ constexpr DefaultConstructible() = delete;
+ constexpr DefaultConstructible(int) {}
+};
+
+template <bool is_copy_constructible>
+struct CopyConstructible {};
+
+template <>
+struct CopyConstructible<false> {
+ constexpr CopyConstructible() = default;
+ constexpr CopyConstructible(const CopyConstructible&) = delete;
+ constexpr CopyConstructible(CopyConstructible&&) = default;
+ CopyConstructible& operator=(const CopyConstructible&) = default;
+ CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <bool is_move_constructible>
+struct MoveConstructible {};
+
+template <>
+struct MoveConstructible<false> {
+ constexpr MoveConstructible() = default;
+ constexpr MoveConstructible(const MoveConstructible&) = default;
+ constexpr MoveConstructible(MoveConstructible&&) = delete;
+ MoveConstructible& operator=(const MoveConstructible&) = default;
+ MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <bool is_copy_assignable>
+struct CopyAssignable {};
+
+template <>
+struct CopyAssignable<false> {
+ constexpr CopyAssignable() = default;
+ constexpr CopyAssignable(const CopyAssignable&) = default;
+ constexpr CopyAssignable(CopyAssignable&&) = default;
+ CopyAssignable& operator=(const CopyAssignable&) = delete;
+ CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <bool is_move_assignable>
+struct MoveAssignable {};
+
+template <>
+struct MoveAssignable<false> {
+ constexpr MoveAssignable() = default;
+ constexpr MoveAssignable(const MoveAssignable&) = default;
+ constexpr MoveAssignable(MoveAssignable&&) = default;
+ MoveAssignable& operator=(const MoveAssignable&) = default;
+ MoveAssignable& operator=(MoveAssignable&&) = delete;
+};
+
+} // namespace internal
+
+// TODO(rkjnsn): Add [[nodiscard]] once C++17 is allowed.
+template <typename SuccessType_, typename ErrorType_>
+class Result : public internal::DefaultConstructible<
+ std::is_default_constructible<SuccessType_>::value>,
+ public internal::CopyConstructible<
+ std::is_copy_constructible<SuccessType_>::value &&
+ std::is_copy_constructible<ErrorType_>::value>,
+ public internal::MoveConstructible<
+ std::is_move_constructible<SuccessType_>::value &&
+ std::is_move_constructible<ErrorType_>::value>,
+ public internal::CopyAssignable<
+ std::is_copy_assignable<SuccessType_>::value &&
+ std::is_copy_assignable<ErrorType_>::value>,
+ public internal::MoveAssignable<
+ std::is_move_assignable<SuccessType_>::value &&
+ std::is_move_assignable<ErrorType_>::value> {
+ public:
+ typedef SuccessType_ SuccessType;
+ typedef ErrorType_ ErrorType;
+
+ private:
+ template <typename T>
+ struct is_convertible_result : public std::false_type {};
+
+ template <typename OtherSuccessType, typename OtherErrorType>
+ struct is_convertible_result<Result<OtherSuccessType, OtherErrorType>>
+ : public std::integral_constant<
+ bool,
+ std::is_convertible<OtherSuccessType&&, SuccessType>::value &&
+ std::is_convertible<OtherErrorType&&, ErrorType>::value> {};
+
+ typedef internal::DefaultConstructible<
+ std::is_default_constructible<SuccessType_>::value>
+ DefaultConstructible;
+
+ public:
+ // Default constructor. Will default construct the success value. This is for
+ // situations like IPC deserialization where a default-constructed instance is
+ // created and the actual value is filled in later. In general, prefer using
+ // Result(kSuccessTag) constructor to be explicit.
+ Result() = default;
+
+ // Direct constructors allow constructing either the SuccessType or ErrorType
+ // in place. Usage: Result(kSuccessTag, success_type_constructor_args...) or
+ // Result(kErrorTag, error_type_constructor_args...)
+ template <typename... Args>
+ Result(typename std::enable_if<
+ std::is_constructible<SuccessType, Args...>::value,
+ SuccessTag>::type,
+ Args&&... args)
+ : DefaultConstructible(0),
+ storage_(kSuccessTag, std::forward<Args>(args)...) {}
+
+ template <typename... Args>
+ Result(
+ typename std::enable_if<std::is_constructible<ErrorType, Args...>::value,
+ ErrorTag>::type,
+ Args&&... args)
+ : DefaultConstructible(0),
+ storage_(kErrorTag, std::forward<Args>(args)...) {}
+
+ // Allow implicit construction from objects implicitly convertible to
+ // SuccessType xor ErrorType.
+ template <typename T,
+ typename std::enable_if<
+ std::is_convertible<T&&, SuccessType>::value &&
+ !std::is_convertible<T&&, ErrorType>::value &&
+ // Prefer move/copy/conversion to member construction.
+ !is_convertible_result<typename std::decay<T>::type>::value,
+ int>::type = 0>
+ Result(T&& success_value)
+ : Result(kSuccessTag, std::forward<T>(success_value)) {}
+
+ template <typename T,
+ typename std::enable_if<
+ !std::is_convertible<T&&, SuccessType>::value &&
+ std::is_convertible<T&&, ErrorType>::value &&
+ !is_convertible_result<typename std::decay<T>::type>::value,
+ int>::type = 0>
+ Result(T&& error_value) : Result(kErrorTag, std::forward<T>(error_value)) {}
+
+ // Copy / move constructors.
+ Result(const Result& other) = default;
+ Result(Result&& other) = default;
+
+ // Conversion constructors.
+ template <
+ typename OtherSuccessType,
+ typename OtherErrorType,
+ typename std::enable_if<
+ std::is_convertible<const OtherSuccessType&, SuccessType>::value &&
+ std::is_convertible<const OtherErrorType&, ErrorType>::value,
+ int>::type = 0>
+ Result(const Result<OtherSuccessType, OtherErrorType>& other)
+ : DefaultConstructible(0), storage_(other.storage_) {}
+
+ template <typename OtherSuccessType,
+ typename OtherErrorType,
+ typename std::enable_if<
+ std::is_convertible<OtherSuccessType&&, SuccessType>::value &&
+ std::is_convertible<OtherErrorType&&, ErrorType>::value,
+ int>::type = 0>
+ Result(Result<OtherSuccessType, OtherErrorType>&& other)
+ : DefaultConstructible(0), storage_(std::move(other.storage_)) {}
+
+ // Assignment.
+ Result& operator=(const Result& other) = default;
+ Result& operator=(Result&& other) = default;
+
+ // Conversion assignment.
+ template <
+ typename OtherSuccessType,
+ typename OtherErrorType,
+ typename std::enable_if<
+ std::is_convertible<const OtherSuccessType&, SuccessType>::value &&
+ std::is_convertible<const OtherErrorType&, ErrorType>::value,
+ int>::type = 0>
+ Result& operator=(const Result<OtherSuccessType, OtherErrorType>& other) {
+ this->~Result();
+ new (this) Result(other);
+ return *this;
+ }
+
+ template <typename OtherSuccessType,
+ typename OtherErrorType,
+ typename std::enable_if<
+ std::is_convertible<OtherSuccessType&&, SuccessType>::value &&
+ std::is_convertible<OtherErrorType&&, ErrorType>::value,
+ int>::type = 0>
+ Result& operator=(Result<OtherSuccessType, OtherErrorType>&& other) {
+ this->~Result();
+ new (this) Result(std::move(other));
+ return *this;
+ }
+
+ // Emplaces new success value in the result and returns a reference to it.
+ template <typename... Args>
+ typename std::enable_if<std::is_constructible<SuccessType, Args...>::value,
+ SuccessType&>::type
+ EmplaceSuccess(Args&&... args) {
+ this->~Result();
+ new (this) Result(kSuccessTag, std::forward<Args>(args)...);
+ return storage_.success;
+ }
+
+ // Emplaces new error value in the result and returns a reference to it.
+ template <typename... Args>
+ typename std::enable_if<std::is_constructible<ErrorType, Args...>::value,
+ ErrorType&>::type
+ EmplaceError(Args&&... args) {
+ this->~Result();
+ new (this) Result(kErrorTag, std::forward<Args>(args)...);
+ return storage_.error;
+ }
+
+ // Maps Result<Success, Error> to Result<NewSuccess, Error> by applying the
+ // provided Success->NewSuccess functor to the success value, if present. If
+ // this Result contains an error, it will be passed through unchanged and the
+ // functor will not be called.
+ template <typename SuccessFunctor>
+ Result<typename std::result_of<SuccessFunctor && (const SuccessType&)>::type,
+ ErrorType>
+ Map(SuccessFunctor&& on_success) const& {
+ if (storage_.is_success) {
+ return {kSuccessTag,
+ std::forward<SuccessFunctor>(on_success)(storage_.success)};
+ } else {
+ return {kErrorTag, storage_.error};
+ }
+ }
+
+ template <typename SuccessFunctor>
+ Result<typename std::result_of<SuccessFunctor && (SuccessType &&)>::type,
+ ErrorType>
+ Map(SuccessFunctor&& on_success) && {
+ if (storage_.is_success) {
+ return {kSuccessTag, std::forward<SuccessFunctor>(on_success)(
+ std::move(storage_.success))};
+ } else {
+ return {kErrorTag, std::move(storage_.error)};
+ }
+ }
+
+ // Maps Result<Success, Error> to Result<Success, NewError> by applying the
+ // provided Error->NewError functor to the error value, if present. If this
+ // Result contains a success value, it will be passed through unchanged and
+ // the functor will not be called.
+ template <typename ErrorFunctor>
+ Result<SuccessType,
+ typename std::result_of<ErrorFunctor && (const ErrorType&)>::type>
+ MapError(ErrorFunctor&& on_error) const& {
+ if (storage_.is_success) {
+ return {kSuccessTag, storage_.success};
+ } else {
+ return {kErrorTag, std::forward<ErrorFunctor>(on_error)(storage_.error)};
+ }
+ }
+
+ template <typename ErrorFunctor>
+ Result<SuccessType,
+ typename std::result_of<ErrorFunctor && (ErrorType &&)>::type>
+ MapError(ErrorFunctor&& on_error) && {
+ if (storage_.is_success) {
+ return {kSuccessTag, std::move(storage_.success)};
+ } else {
+ return {kErrorTag,
+ std::forward<ErrorFunctor>(on_error)(std::move(storage_.error))};
+ }
+ }
+
+ // Maps Result<Success, Error> to Result<NewSuccess, Error> by calling the
+ // provided Success->Result<NewSuccess, Error> functor with the success value,
+ // if present. If this Result contains an error, it will be passed through
+ // unchanged and the functor will not be called.
+ template <
+ typename SuccessFunctor,
+ typename ReturnType =
+ typename std::result_of<SuccessFunctor && (const SuccessType&)>::type,
+ typename std::enable_if<
+ std::is_convertible<typename ReturnType::ErrorType, ErrorType>::value,
+ int>::type = 0>
+ Result<typename ReturnType::SuccessType, ErrorType> AndThen(
+ SuccessFunctor&& on_success) const& {
+ if (storage_.is_success) {
+ return std::forward<SuccessFunctor>(on_success)(storage_.success);
+ } else {
+ return {kErrorTag, storage_.error};
+ }
+ }
+
+ template <
+ typename SuccessFunctor,
+ typename ReturnType =
+ typename std::result_of<SuccessFunctor && (SuccessType &&)>::type,
+ typename std::enable_if<
+ std::is_convertible<typename ReturnType::ErrorType, ErrorType>::value,
+ int>::type = 0>
+ Result<typename ReturnType::SuccessType, ErrorType> AndThen(
+ SuccessFunctor&& on_success) && {
+ if (storage_.is_success) {
+ return std::forward<SuccessFunctor>(on_success)(
+ std::move(storage_.success));
+ } else {
+ return {kErrorTag, std::move(storage_.error)};
+ }
+ }
+
+ // Maps Result<Success, Error> to Result<Success, NewError> by calling the
+ // provided Error->Result<Success, NewError> functor with the error value, if
+ // present. If this Result contains a success value, it will be passed through
+ // unchanged and the functor will not be called.
+ template <typename ErrorFunctor,
+ typename ReturnType = typename std::result_of<
+ ErrorFunctor && (const ErrorType&)>::type,
+ typename std::enable_if<
+ std::is_convertible<typename ReturnType::SuccessType,
+ SuccessType>::value,
+ int>::type = 0>
+ Result<SuccessType, typename ReturnType::ErrorType> OrElse(
+ ErrorFunctor&& on_error) const& {
+ if (storage_.is_success) {
+ return {kSuccessTag, storage_.success};
+ } else {
+ return std::forward<ErrorFunctor>(on_error)(storage_.error);
+ }
+ }
+
+ template <typename ErrorFunctor,
+ typename ReturnType =
+ typename std::result_of<ErrorFunctor && (ErrorType &&)>::type,
+ typename std::enable_if<
+ std::is_convertible<typename ReturnType::SuccessType,
+ SuccessType>::value,
+ int>::type = 0>
+ Result<SuccessType, typename ReturnType::ErrorType> OrElse(
+ ErrorFunctor&& on_error) && {
+ if (storage_.is_success) {
+ return {kSuccessTag, std::move(storage_.success)};
+ } else {
+ return std::forward<ErrorFunctor>(on_error)(std::move(storage_.error));
+ }
+ }
+
+ // Calls either success() or error() on the provided visitor depending on the
+ // state of the result, passing the value of the corresponding state.
+ template <
+ typename Visitor,
+ typename SuccessReturn = decltype(
+ std::declval<Visitor>().success(std::declval<const SuccessType&>())),
+ typename ErrorReturn = decltype(
+ std::declval<Visitor>().error(std::declval<const ErrorType&>())),
+ typename std::enable_if<std::is_same<SuccessReturn, ErrorReturn>::value,
+ int>::type = 0>
+ SuccessReturn Visit(Visitor&& visitor) const& {
+ if (storage_.is_success) {
+ return std::forward<Visitor>(visitor).success(storage_.success);
+ } else {
+ return std::forward<Visitor>(visitor).error(storage_.error);
+ }
+ }
+
+ template <
+ typename Visitor,
+ typename SuccessReturn = decltype(
+ std::declval<Visitor>().success(std::declval<SuccessType&>())),
+ typename ErrorReturn =
+ decltype(std::declval<Visitor>().error(std::declval<ErrorType&>())),
+ typename std::enable_if<std::is_same<SuccessReturn, ErrorReturn>::value,
+ int>::type = 0>
+ SuccessReturn Visit(Visitor&& visitor) & {
+ if (storage_.is_success) {
+ return std::forward<Visitor>(visitor).success(storage_.success);
+ } else {
+ return std::forward<Visitor>(visitor).error(storage_.error);
+ }
+ }
+
+ template <
+ typename Visitor,
+ typename SuccessReturn = decltype(
+ std::declval<Visitor>().success(std::declval<SuccessType&&>())),
+ typename ErrorReturn =
+ decltype(std::declval<Visitor>().error(std::declval<ErrorType&&>())),
+ typename std::enable_if<std::is_same<SuccessReturn, ErrorReturn>::value,
+ int>::type = 0>
+ SuccessReturn Visit(Visitor&& visitor) && {
+ if (storage_.is_success) {
+ return std::forward<Visitor>(visitor).success(
+ std::move(storage_.success));
+ } else {
+ return std::forward<Visitor>(visitor).error(std::move(storage_.error));
+ }
+ }
+
+ // Accessors
+
+ bool is_success() const { return storage_.is_success; }
+
+ bool is_error() const { return !storage_.is_success; }
+
+ explicit operator bool() const { return storage_.is_success; }
+
+ SuccessType& success() {
+ DCHECK(storage_.is_success);
+ return storage_.success;
+ }
+
+ const SuccessType& success() const {
+ DCHECK(storage_.is_success);
+ return storage_.success;
+ }
+
+ ErrorType& error() {
+ DCHECK(!storage_.is_success);
+ return storage_.error;
+ }
+
+ const ErrorType& error() const {
+ DCHECK(!storage_.is_success);
+ return storage_.error;
+ }
+
+ private:
+ internal::ResultStorage<SuccessType, ErrorType> storage_;
+
+ template <typename S, typename E>
+ friend class Result;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_RESULT_H_
diff --git a/remoting/base/result_unittest.cc b/remoting/base/result_unittest.cc
new file mode 100644
index 0000000..b1bea5b5
--- /dev/null
+++ b/remoting/base/result_unittest.cc
@@ -0,0 +1,410 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "remoting/base/result.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+TEST(Result, DefaultConstruction) {
+ struct DefaultConstruct {
+ std::string value;
+ DefaultConstruct() : value("value1") {}
+ };
+
+ Result<DefaultConstruct, int> result;
+ ASSERT_TRUE(result.is_success());
+ EXPECT_EQ("value1", result.success().value);
+}
+
+namespace {
+struct NotDefaultConstructible {
+ NotDefaultConstructible(int);
+};
+} // namespace
+
+static_assert(
+ !std::is_default_constructible<Result<NotDefaultConstructible, int>>::value,
+ "Should not be default constructible if success type isn't.");
+
+TEST(Result, TaggedSuccessConstruction) {
+ Result<std::string, int> result(kSuccessTag, 5, 'a');
+ ASSERT_TRUE(result.is_success());
+ ASSERT_FALSE(result.is_error());
+ EXPECT_EQ("aaaaa", result.success());
+}
+
+TEST(Result, TaggedErrorConstruction) {
+ Result<std::string, int> result(kErrorTag, 2);
+ ASSERT_FALSE(result.is_success());
+ ASSERT_TRUE(result.is_error());
+ EXPECT_EQ(2, result.error());
+}
+
+static_assert(
+ !std::is_constructible<Result<std::string, int>, SuccessTag, float>::value,
+ "Invalid constructor parameters should trigger SFINAE.");
+
+TEST(Result, ImplicitSuccessConstruction) {
+ Result<std::string, int> result = "value3";
+ ASSERT_TRUE(result.is_success());
+ EXPECT_EQ("value3", result.success());
+}
+
+TEST(Result, ImplicitErrorConstruction) {
+ Result<std::string, int> result = 3;
+ ASSERT_TRUE(result.is_error());
+ EXPECT_EQ(3, result.error());
+}
+
+static_assert(!std::is_constructible<Result<int, float>, int>::value,
+ "Should not allow ambiguous untagged construction.");
+
+TEST(Result, SuccessCopyConstruction) {
+ Result<std::string, int> result1 = "value4";
+ Result<std::string, int> result2 = result1;
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value4", result2.success());
+ // Ensure result1 wasn't modified.
+ EXPECT_EQ("value4", result1.success());
+}
+
+TEST(Result, ErrorCopyConstruction) {
+ Result<int, std::string> result1 = "value5";
+ Result<int, std::string> result2 = result1;
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ("value5", result2.error());
+ // Ensure result1 wasn't modified.
+ EXPECT_EQ("value5", result1.error());
+}
+
+static_assert(
+ !std::is_copy_constructible<Result<std::unique_ptr<int>, int>>::value &&
+ !std::is_copy_constructible<Result<int, std::unique_ptr<int>>>::value,
+ "Should not be copy constructible if either type isn't.");
+
+TEST(Result, SuccessMoveConstruction) {
+ Result<std::unique_ptr<std::string>, int> result1 =
+ std::make_unique<std::string>("value6");
+ Result<std::unique_ptr<std::string>, int> result2 = std::move(result1);
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value6", *result2.success());
+ EXPECT_TRUE(result1.is_success());
+ EXPECT_FALSE(result1.success());
+}
+
+TEST(Result, ErrorMoveConstruction) {
+ Result<int, std::unique_ptr<std::string>> result1 =
+ std::make_unique<std::string>("value7");
+ Result<int, std::unique_ptr<std::string>> result2 = std::move(result1);
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ("value7", *result2.error());
+ EXPECT_TRUE(result1.is_error());
+ EXPECT_FALSE(result1.error());
+}
+
+TEST(Result, SuccessCopyConversion) {
+ const char* value = "value8";
+ Result<const char*, int> result1 = value;
+ Result<std::string, int> result2 = result1;
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value8", result2.success());
+ EXPECT_EQ(value, result1.success());
+}
+
+TEST(Result, ErrorCopyConversion) {
+ const char* value = "value9";
+ Result<int, const char*> result1 = value;
+ Result<int, std::string> result2 = result1;
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ("value9", result2.error());
+ EXPECT_EQ(value, result1.error());
+}
+
+TEST(Result, SuccessMoveConversion) {
+ struct Deleter {
+ Deleter(const std::default_delete<std::string>&) {}
+ void operator()(void* ptr) { delete static_cast<std::string*>(ptr); }
+ };
+
+ Result<std::unique_ptr<std::string>, int> result1 =
+ std::make_unique<std::string>("value10");
+ Result<std::unique_ptr<void, Deleter>, int> result2 = std::move(result1);
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value10", *static_cast<std::string*>(result2.success().get()));
+}
+
+TEST(Result, ErrorMoveConversion) {
+ struct Deleter {
+ Deleter(const std::default_delete<std::string>&) {}
+ void operator()(void* ptr) { delete static_cast<std::string*>(ptr); }
+ };
+
+ Result<int, std::unique_ptr<std::string>> result1 =
+ std::make_unique<std::string>("value11");
+ Result<int, std::unique_ptr<void, Deleter>> result2 = std::move(result1);
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ("value11", *static_cast<std::string*>(result2.error().get()));
+}
+
+TEST(Result, Destruction) {
+ class DestructIncrement {
+ public:
+ explicit DestructIncrement(int* variable) : variable_(variable) {}
+ ~DestructIncrement() { ++(*variable_); }
+
+ private:
+ int* variable_;
+ };
+
+ int success_count = 0;
+ int error_count = 0;
+
+ Result<DestructIncrement, int>(kSuccessTag, &success_count);
+ EXPECT_EQ(1, success_count);
+ EXPECT_EQ(0, error_count);
+
+ Result<int, DestructIncrement>(kErrorTag, &error_count);
+ EXPECT_EQ(1, success_count);
+ EXPECT_EQ(1, error_count);
+}
+
+TEST(Result, CopyAssignment) {
+ Result<std::string, int> result1 = "value12";
+ Result<std::string, int> result2 = 0;
+ result2 = result1;
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value12", result2.success());
+
+ static_assert(
+ !std::is_copy_assignable<Result<std::unique_ptr<int>, int>>::value &&
+ !std::is_copy_assignable<Result<int, std::unique_ptr<int>>>::value,
+ "Should not be copy assignable if either type isn't.");
+}
+
+TEST(Result, MoveAssignment) {
+ Result<std::unique_ptr<std::string>, int> result1 =
+ std::make_unique<std::string>("value13");
+ Result<std::unique_ptr<std::string>, int> result2 = 0;
+ result2 = std::move(result1);
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value13", *result2.success());
+}
+
+TEST(Result, CopyConversionAssignment) {
+ const char* value1 = "value14";
+ Result<const char*, int> result1 = value1;
+ Result<std::string, int> result2 = 0;
+ result2 = result1;
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value14", result2.success());
+}
+
+TEST(Result, MoveConversionAssignment) {
+ struct Deleter {
+ Deleter(const std::default_delete<std::string>&) {}
+ void operator()(void* ptr) { delete static_cast<std::string*>(ptr); }
+ };
+
+ Result<std::unique_ptr<std::string>, int> result1 =
+ std::make_unique<std::string>("value15");
+ Result<std::unique_ptr<void, Deleter>, int> result2 = 0;
+ result2 = std::move(result1);
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value15", *static_cast<std::string*>(result2.success().get()));
+}
+
+TEST(Result, EmplaceSuccess) {
+ Result<std::string, int> result = 0;
+ result.EmplaceSuccess(5, 'p');
+ ASSERT_TRUE(result.is_success());
+ EXPECT_EQ("ppppp", result.success());
+}
+
+TEST(Result, EmplaceError) {
+ Result<std::string, int> result;
+ result.EmplaceError(17);
+ ASSERT_TRUE(result.is_error());
+ EXPECT_EQ(17, result.error());
+}
+
+TEST(Result, MapLvalue) {
+ Result<std::string, int> result1 = "value18";
+ Result<const char*, int> result2 =
+ result1.Map([](const std::string& value) { return value.c_str(); });
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_TRUE(strcmp("value18", result2.success()) == 0);
+}
+
+TEST(Result, MapRvalue) {
+ Result<std::unique_ptr<std::string>, int> result1 =
+ std::make_unique<std::string>("value19");
+ auto result2 = std::move(result1).Map(
+ [](std::unique_ptr<std::string>&& value) { return std::move(*value); });
+ static_assert(
+ std::is_same<decltype(result2), Result<std::string, int>>::value,
+ "Incorrect type inferred.");
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value19", result2.success());
+}
+
+TEST(Result, MapPassesErrorThrough) {
+ Result<std::string, int> result1 = 20;
+ Result<const char*, int> result2 =
+ result1.Map([](const std::string& value) { return value.c_str(); });
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ(20, result2.error());
+}
+
+TEST(Result, MapErrorLvalue) {
+ Result<std::string, int> result1 = 21;
+ Result<std::string, int> result2 =
+ result1.MapError([](int value) { return value + 1; });
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ(22, result2.error());
+}
+
+TEST(Result, MapErrorRvalue) {
+ Result<int, std::unique_ptr<std::string>> result1 =
+ std::make_unique<std::string>("value23");
+ auto result2 =
+ std::move(result1).MapError([](std::unique_ptr<std::string>&& value) {
+ return std::make_unique<std::size_t>(value->size());
+ });
+ static_assert(std::is_same<decltype(result2),
+ Result<int, std::unique_ptr<std::size_t>>>::value,
+ "Incorrect type inferred.");
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ(7UL, *result2.error());
+}
+
+TEST(Result, MapErrorPassesSuccessThrough) {
+ Result<std::string, int> result1 = "value24";
+ Result<std::string, float> result2 =
+ result1.MapError([](int value) { return value * 2.0; });
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value24", result2.success());
+}
+
+TEST(Result, AndThenLvalue) {
+ Result<std::string, int> result1 = "value25";
+ auto result2 = result1.AndThen(
+ [](const std::string&) { return Result<const char*, float>(26.0); });
+ static_assert(
+ std::is_same<decltype(result2), Result<const char*, int>>::value,
+ "Error type should stay the same.");
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ(26, result2.error());
+}
+
+TEST(Result, AndThenRvalue) {
+ Result<std::unique_ptr<std::string>, int> result1 =
+ std::make_unique<std::string>("value27");
+ auto result2 =
+ std::move(result1).AndThen([](std::unique_ptr<std::string>&& value) {
+ return Result<std::string, int>(std::move(*value));
+ });
+ static_assert(
+ std::is_same<decltype(result2), Result<std::string, int>>::value,
+ "Incorrect type inferred.");
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value27", result2.success());
+}
+
+TEST(Result, AndThenPassesErrorThrough) {
+ Result<std::string, int> result1 = 28;
+ Result<int, int> result2 = result1.AndThen(
+ [](const std::string&) { return Result<int, int>(kSuccessTag, 29); });
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ(28, result2.error());
+}
+
+TEST(Result, OrElseLvalue) {
+ Result<std::string, int> result1 = 30;
+ Result<std::string, int> result2 =
+ result1.OrElse([](int) -> Result<std::string, int> { return "value31"; });
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value31", result2.success());
+}
+
+TEST(Result, OrElseRvalue) {
+ Result<int, std::unique_ptr<std::string>> result1 =
+ std::make_unique<std::string>("value32");
+ Result<int, std::string> result2 =
+ std::move(result1).OrElse([](std::unique_ptr<std::string>&&) {
+ return Result<int, std::string>("value33");
+ });
+ ASSERT_TRUE(result2.is_error());
+ EXPECT_EQ("value33", result2.error());
+}
+
+TEST(Result, OrElsePassesSuccessThrough) {
+ Result<std::string, int> result1 = "value34";
+ Result<std::string, float> result2 = result1.OrElse(
+ [](int value) -> Result<std::string, float> { return value * 2.0; });
+ ASSERT_TRUE(result2.is_success());
+ EXPECT_EQ("value34", result2.success());
+}
+
+TEST(Result, Visit) {
+ struct Visitor {
+ char success(const std::string& value) {
+ EXPECT_EQ("value35", value);
+ return '\1';
+ }
+ bool success(std::string& value) {
+ EXPECT_EQ("value35", value);
+ return true;
+ }
+ int success(std::string&& value) {
+ EXPECT_EQ("value35", value);
+ return 1;
+ }
+ char error(const std::wstring& value) {
+ EXPECT_EQ(L"value36", value);
+ return '\0';
+ }
+ bool error(std::wstring& value) {
+ EXPECT_EQ(L"value36", value);
+ return false;
+ }
+ int error(std::wstring&& value) {
+ EXPECT_EQ(L"value36", value);
+ return 0;
+ }
+ };
+
+ Result<std::string, std::wstring> result;
+
+ result.EmplaceSuccess("value35");
+ auto success1 =
+ const_cast<const Result<std::string, std::wstring>&>(result).Visit(
+ Visitor());
+ static_assert(std::is_same<char, decltype(success1)>::value,
+ "Return type should be char.");
+ EXPECT_EQ('\1', success1);
+ auto success2 = result.Visit(Visitor());
+ static_assert(std::is_same<bool, decltype(success2)>::value,
+ "Return type should be bool.");
+ EXPECT_EQ(true, success2);
+ auto success3 = std::move(result).Visit(Visitor());
+ static_assert(std::is_same<int, decltype(success3)>::value,
+ "Return type should be int.");
+ EXPECT_EQ(1, success3);
+
+ result.EmplaceError(L"value36");
+ auto error1 =
+ const_cast<const Result<std::string, std::wstring>&>(result).Visit(
+ Visitor());
+ EXPECT_EQ('\0', error1);
+ auto error2 = result.Visit(Visitor());
+ EXPECT_EQ(false, error2);
+ auto error3 = std::move(result).Visit(Visitor());
+ EXPECT_EQ(0, error3);
+}
+
+} // namespace remoting