chromium/remoting/base/result.h

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