blob: 77ceb32aa1488bc0039a390c9350e2e7089e581e [file] [log] [blame]
// 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 (std::addressof(success)) SuccessType(std::forward<Args>(args)...);
}
template <typename... Args>
ResultStorage(ErrorTag, Args&&... args) : is_success(false) {
new (std::addressof(error)) ErrorType(std::forward<Args>(args)...);
}
// Copy/move constructors.
ResultStorage(const ResultStorage& other) : is_success(other.is_success) {
if (is_success) {
new (std::addressof(success)) SuccessType(other.success);
} else {
new (std::addressof(error)) ErrorType(other.error);
}
}
ResultStorage(ResultStorage&& other) : is_success(other.is_success) {
if (is_success) {
new (std::addressof(success)) SuccessType(std::move(other.success));
} else {
new (std::addressof(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 (std::addressof(success)) SuccessType(other.success);
} else {
new (std::addressof(error)) ErrorType(other.error);
}
}
template <typename S, typename E>
ResultStorage(ResultStorage<S, E>&& other) : is_success(other.is_success) {
if (is_success) {
new (std::addressof(success)) SuccessType(std::move(other.success));
} else {
new (std::addressof(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; }
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;
}
// Allow Result to be treated like an Optional that just happens to have more
// details in the error case.
explicit operator bool() const { return storage_.is_success; }
SuccessType& operator*() { return success(); }
const SuccessType& operator*() const { return success(); }
SuccessType* operator->() { return &success(); }
const SuccessType* operator->() const { return &success(); }
private:
internal::ResultStorage<SuccessType, ErrorType> storage_;
template <typename S, typename E>
friend class Result;
};
} // namespace remoting
#endif // REMOTING_BASE_RESULT_H_