// Copyright 2018 The Chromium Authors // 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 <optional> #include <type_traits> #include <utility> #include "base/check.h" #include "third_party/abseil-cpp/absl/types/variant.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-constructed 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 { // TODO(joedow): Migrate instances of remoting::Result to base::expected. // SuccessTag and ErrorTag are used for constructing a Result in the success // state or error state, respectively. class SuccessTag { … }; class ErrorTag { … }; // absl::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 std::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. constexpr SuccessTag kSuccessTag = …; constexpr ErrorTag kErrorTag = …; constexpr absl::monostate kMonostate = …; namespace internal { template <typename SuccessType, typename ErrorType> struct ResultStorage { … }; // 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 { … }; template <> struct DefaultConstructible<false> { … }; template <bool is_copy_constructible> struct CopyConstructible { … }; template <> struct CopyConstructible<false> { … }; template <bool is_move_constructible> struct MoveConstructible { … }; template <> struct MoveConstructible<false> { … }; template <bool is_copy_assignable> struct CopyAssignable { … }; template <> struct CopyAssignable<false> { … }; template <bool is_move_assignable> struct MoveAssignable { … }; template <> struct MoveAssignable<false> { … }; } // 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> { … }; } // namespace remoting #endif // REMOTING_BASE_RESULT_H_