#ifndef REMOTING_BASE_RESULT_H_
#define REMOTING_BASE_RESULT_H_
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
namespace remoting {
class SuccessTag {};
class ErrorTag {};
constexpr SuccessTag kSuccessTag = SuccessTag();
constexpr ErrorTag kErrorTag = ErrorTag();
constexpr absl::monostate kMonostate = absl::monostate();
namespace internal {
template <typename SuccessType, typename ErrorType>
struct ResultStorage {
ResultStorage() : ResultStorage(kSuccessTag) {}
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)...);
}
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));
}
}
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();
}
}
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;
};
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;
};
}
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:
Result() = default;
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)...) {}
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&& 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)) {}
Result(const Result& other) = default;
Result(Result&& other) = default;
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_)) {}
Result& operator=(const Result& other) = default;
Result& operator=(Result&& other) = default;
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;
}
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;
}
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;
}
template <typename SuccessFunctor>
Result<
typename std::invoke_result<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::invoke_result<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)};
}
}
template <typename ErrorFunctor>
Result<SuccessType,
typename std::invoke_result<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::invoke_result<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))};
}
}
template <
typename SuccessFunctor,
typename ReturnType = typename std::
invoke_result<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::invoke_result<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)};
}
}
template <
typename ErrorFunctor,
typename ReturnType =
typename std::invoke_result<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::invoke_result<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));
}
}
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));
}
}
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;
}
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;
};
}
#endif