blob: 50949c98acdc402c65d71c0110619f278e7d3186 [file] [log] [blame]
// Copyright 2019 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 BASE_TASK_PROMISE_PROMISE_RESULT_H_
#define BASE_TASK_PROMISE_PROMISE_RESULT_H_
#include <type_traits>
#include "base/containers/unique_any.h"
namespace base {
// An optional return type from a promise callback, which allows the callback to
// decide whether or not it should reject. E.g.
//
// enum class Error { kReason };
//
// PromiseResult<int, Error> MyFn() {
// int result;
// ...
// if (error)
// return Error::kReason;
// return result;
// }
//
// If ResolveType and RejectType are distinct PromiseResult's constructor will
// accept them. E.g.
//
// PromiseResult<int, std::string> pr(123); // Resolve
// PromiseResult<int, std::string> pr("whoops"); // Reject
//
// If ResolveType and RejectType are the same you need to use Resolved<> and
// Rejected<> to disambiguate. E.g.
//
// PromiseResult<int, int> pr(Resolved{123}); // Resolve
// PromiseResult<int, int> pr(Rejected{123}); // Reject
//
// PromiseResult<ResolveType, RejectType> has a constructor that accepts
// Promise<ResolveType, RejectType>.
template <typename ResolveType, typename RejectType = NoReject>
class PromiseResult {
public:
template <typename ResolveT = ResolveType,
typename RejectT = RejectType,
class Enable = std::enable_if<std::is_void<ResolveT>::value ^
std::is_void<RejectT>::value>>
PromiseResult() : PromiseResult(typename Analyze<void>::TagType()) {}
template <typename T>
PromiseResult(T&& t)
: PromiseResult(typename Analyze<std::decay_t<T>>::TagType(),
std::forward<T>(t)) {}
PromiseResult(PromiseResult&& other) noexcept = default;
PromiseResult(const PromiseResult&) = delete;
PromiseResult& operator=(const PromiseResult&) = delete;
unique_any& value() { return value_; }
private:
struct IsWrapped {};
struct IsResolved {};
struct IsRejected {};
struct IsPromise {};
// Helper that assigns one of the above tags for |T|.
template <typename T>
struct Analyze {
using DecayedT = std::decay_t<T>;
static constexpr bool is_resolve =
std::is_convertible<DecayedT, std::decay_t<ResolveType>>::value;
static constexpr bool is_reject =
std::is_convertible<DecayedT, std::decay_t<RejectType>>::value;
static_assert(!is_reject || !std::is_same<DecayedT, NoReject>::value,
"A NoReject promise can't reject");
static_assert(!std::is_same<std::decay_t<ResolveType>,
std::decay_t<RejectType>>::value,
"Ambiguous because ResolveType and RejectType are the same");
static_assert(is_resolve || is_reject,
"Argument matches neither resolve nor reject type.");
static_assert(
is_resolve != is_reject && (is_resolve || is_reject),
"Ambiguous because argument matches both ResolveType and RejectType");
using TagType = std::conditional_t<is_resolve, IsResolved, IsRejected>;
};
template <typename T>
struct Analyze<Resolved<T>> {
using TagType = IsWrapped;
static_assert(std::is_same<ResolveType, T>::value,
"T in Resolved<T> is not ResolveType");
};
template <typename T>
struct Analyze<Rejected<T>> {
static_assert(std::is_same<RejectType, T>::value,
"T in Rejected<T> is not RejectType");
static_assert(!std::is_same<RejectType, NoReject>::value,
"A NoReject promise can't reject");
using TagType = IsWrapped;
};
template <typename ResolveT, typename RejectT>
struct Analyze<Promise<ResolveT, RejectT>> {
using TagType = IsPromise;
static_assert(std::is_same<ResolveT, ResolveType>::value,
"Promise resolve types don't match");
static_assert(std::is_same<RejectT, RejectType>::value,
"Promise reject types don't match");
};
template <typename... Args>
PromiseResult(IsResolved, Args&&... args)
: value_(in_place_type_t<Resolved<ResolveType>>(),
std::forward<Args>(args)...) {}
template <typename... Args>
PromiseResult(IsRejected, Args&&... args)
: value_(in_place_type_t<Rejected<RejectType>>(),
std::forward<Args>(args)...) {}
PromiseResult(IsPromise, const Promise<ResolveType, RejectType>& promise)
: value_(promise.abstract_promise_) {}
template <typename T>
PromiseResult(IsWrapped, T&& t) : value_(std::forward<T>(t)) {}
unique_any value_;
};
} // namespace base
#endif // BASE_TASK_PROMISE_PROMISE_RESULT_H_