| // 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_ |