Commit fcc5e7c2 authored by danakj's avatar danakj Committed by Commit Bot

Add *Callback::Then() to chain 2 callbacks together

a.Then(b) will return a new callback that when Run() will
1) run |a|
2) run |b|, passing it the return value from a
3) return the result from |b|

OnceCallbacks must be destroyed when joining them together with Then(),
so the method is rvalue-qualified. This means it is used in the
same way as Run(), for example, this function posts two callbacks to
run together as a single task:

void PostTwoTasks(base::OnceClosure c1, base::OnceClosure c2) {
  PostTask(std::move(c1).Then(std::move(c2)));
}

RepeatingCallback can be joined destructively via the rvalue-qualified
overload, or non-destructively otherwise. The latter is allowed for a
RepeatingCallback because it is meant to have multiple callers and
therefore having both the original callbacks and the joined callback
pointing to the same underlying functor is not problematic.

R=chrisha@chromium.org, gab@chromium.org

Bug: 1140582
Change-Id: Ie147f01d1c8adeb5ed34e4933f211e7d247e3c6b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2485642
Commit-Queue: danakj <danakj@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820319}
parent fd20b780
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <stddef.h> #include <stddef.h>
#include "base/bind.h"
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/callback_internal.h" #include "base/callback_internal.h"
#include "base/notreached.h" #include "base/notreached.h"
...@@ -99,6 +100,31 @@ class OnceCallback<R(Args...)> : public internal::CallbackBase { ...@@ -99,6 +100,31 @@ class OnceCallback<R(Args...)> : public internal::CallbackBase {
reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke()); reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
return f(cb.bind_state_.get(), std::forward<Args>(args)...); return f(cb.bind_state_.get(), std::forward<Args>(args)...);
} }
// Then() returns a new OnceCallback that receives the same arguments as
// |this|, and with the return type of |then|. The returned callback will:
// 1) Run the functor currently bound to |this| callback.
// 2) Run the |then| callback with the result from step 1 as its single
// argument.
// 3) Return the value from running the |then| callback.
//
// Since this method generates a callback that is a replacement for `this`,
// `this` will be consumed and reset to a null callback to ensure the
// originally-bound functor can be run at most once.
template <typename U, typename R2 = internal::ExtractReturnType<U>>
OnceCallback<R2(Args...)> Then(OnceCallback<U> then) && {
using ThenCallbackArgs = internal::ExtractArgs<U>;
static_assert(
(std::is_void<R>::value &&
std::is_same<internal::TypeList<>, ThenCallbackArgs>::value) ||
std::is_same<internal::TypeList<R>, ThenCallbackArgs>::value,
"The |then| callback must accept the return value from the original "
"callback as its only parameter.");
CHECK(then);
return BindOnce(
internal::ThenHelper<OnceCallback, OnceCallback<U>, R, Args...>(),
std::move(*this), std::move(then));
}
}; };
template <typename R, typename... Args> template <typename R, typename... Args>
...@@ -145,6 +171,49 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable { ...@@ -145,6 +171,49 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke()); reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...); return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
} }
// Then() returns a new RepeatingCallback that receives the same arguments as
// |this|, and with the return type of |then|. The
// returned callback will:
// 1) Run the functor currently bound to |this| callback.
// 2) Run the |then| callback with the result from step 1 as its single
// argument.
// 3) Return the value from running the |then| callback.
//
// If called on an rvalue (e.g. std::move(cb).Then(...)), this method
// generates a callback that is a replacement for `this`. Therefore, `this`
// will be consumed and reset to a null callback to ensure the
// originally-bound functor can be run at most once.
template <typename U, typename R2 = internal::ExtractReturnType<U>>
RepeatingCallback<R2(Args...)> Then(RepeatingCallback<U> then) const& {
using ThenCallbackArgs = internal::ExtractArgs<U>;
static_assert(
(std::is_void<R>::value &&
std::is_same<internal::TypeList<>, ThenCallbackArgs>::value) ||
std::is_same<internal::TypeList<R>, ThenCallbackArgs>::value,
"The |then| callback must accept the return value from the original "
"callback as its only parameter.");
CHECK(then);
return BindRepeating(
internal::ThenHelper<RepeatingCallback, RepeatingCallback<U>, R,
Args...>(),
*this, std::move(then));
}
template <typename U, typename R2 = internal::ExtractReturnType<U>>
RepeatingCallback<R2(Args...)> Then(RepeatingCallback<U> then) && {
using ThenCallbackArgs = internal::ExtractArgs<U>;
static_assert(
(std::is_void<R>::value &&
std::is_same<internal::TypeList<>, ThenCallbackArgs>::value) ||
std::is_same<internal::TypeList<R>, ThenCallbackArgs>::value,
"The |then| callback must accept the return value from the original "
"callback as its only parameter.");
CHECK(then);
return BindRepeating(
internal::ThenHelper<RepeatingCallback, RepeatingCallback<U>, R,
Args...>(),
std::move(*this), std::move(then));
}
}; };
} // namespace base } // namespace base
......
...@@ -188,6 +188,30 @@ class BASE_EXPORT CallbackBaseCopyable : public CallbackBase { ...@@ -188,6 +188,30 @@ class BASE_EXPORT CallbackBaseCopyable : public CallbackBase {
~CallbackBaseCopyable() = default; ~CallbackBaseCopyable() = default;
}; };
// Non-void return type is passed to the |then| callback.
template <typename CallbackType,
typename ThenClosureType,
typename R,
typename... Args,
std::enable_if_t<!std::is_void<R>::value, int> = 0>
auto ThenHelper() {
return [](CallbackType c1, ThenClosureType c2, Args... c1_args) {
return std::move(c2).Run(std::move(c1).Run(std::forward<Args>(c1_args)...));
};
}
// Void return type means nothing is passed to the |then| callback.
template <typename CallbackType,
typename ThenClosureType,
typename R,
typename... Args,
std::enable_if_t<std::is_void<R>::value, int> = 0>
auto ThenHelper() {
return [](CallbackType c1, ThenClosureType c2, Args... c1_args) {
std::move(c1).Run(std::forward<Args>(c1_args)...);
return std::move(c2).Run();
};
}
} // namespace internal } // namespace internal
} // namespace base } // namespace base
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/callback_internal.h" #include "base/callback_internal.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/test_timeouts.h" #include "base/test/test_timeouts.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -169,6 +170,437 @@ TEST_F(CallbackTest, MaybeValidReturnsTrue) { ...@@ -169,6 +170,437 @@ TEST_F(CallbackTest, MaybeValidReturnsTrue) {
EXPECT_TRUE(cb.MaybeValid()); EXPECT_TRUE(cb.MaybeValid());
} }
TEST_F(CallbackTest, ThenResetsOriginalCallback) {
{
// OnceCallback::Then() always destroys the original callback.
OnceClosure orig = base::BindOnce([]() {});
EXPECT_TRUE(!!orig);
OnceClosure joined = std::move(orig).Then(base::BindOnce([]() {}));
EXPECT_TRUE(!!joined);
EXPECT_FALSE(!!orig);
}
{
// RepeatingCallback::Then() destroys the original callback if it's an
// rvalue.
RepeatingClosure orig = base::BindRepeating([]() {});
EXPECT_TRUE(!!orig);
RepeatingClosure joined =
std::move(orig).Then(base::BindRepeating([]() {}));
EXPECT_TRUE(!!joined);
EXPECT_FALSE(!!orig);
}
{
// RepeatingCallback::Then() doesn't destroy the original callback if it's
// not an rvalue.
RepeatingClosure orig = base::BindRepeating([]() {});
RepeatingClosure copy = orig;
EXPECT_TRUE(!!orig);
RepeatingClosure joined = orig.Then(base::BindRepeating([]() {}));
EXPECT_TRUE(!!joined);
EXPECT_TRUE(!!orig);
// The original callback is not changed.
EXPECT_EQ(orig, copy);
EXPECT_NE(joined, copy);
}
}
// A factory class for building an outer and inner callback for calling
// Then() on either a OnceCallback or RepeatingCallback with combinations of
// void return types, non-void, and move-only return types.
template <bool use_once, typename R, typename ThenR, typename... Args>
class CallbackThenTest;
template <bool use_once, typename R, typename ThenR, typename... Args>
class CallbackThenTest<use_once, R(Args...), ThenR> {
public:
using CallbackType =
typename std::conditional<use_once,
OnceCallback<R(Args...)>,
RepeatingCallback<R(Args...)>>::type;
using ThenType =
typename std::conditional<use_once, OnceClosure, RepeatingClosure>::type;
// Gets the Callback that will have Then() called on it. Has a return type
// of `R`, which would chain to the inner callback for Then(). Has inputs of
// type `Args...`.
static auto GetOuter(std::string& s) {
s = "";
return Bind(
[](std::string* s, Args... args) {
return Outer(s, std::forward<Args>(args)...);
},
&s);
}
// Gets the Callback that will be passed to Then(). Has a return type of
// `ThenR`, specified for the class instance. Receives as input the return
// type `R` from the function bound and returned in GetOuter().
static auto GetInner(std::string& s) { return Bind(&Inner<R, ThenR>, &s); }
private:
template <bool bind_once = use_once,
typename F,
typename... FArgs,
std::enable_if_t<bind_once, int> = 0>
static auto Bind(F function, FArgs... args) {
return BindOnce(function, std::forward<FArgs>(args)...);
}
template <bool bind_once = use_once,
typename F,
typename... FArgs,
std::enable_if_t<!bind_once, int> = 0>
static auto Bind(F function, FArgs... args) {
return BindRepeating(function, std::forward<FArgs>(args)...);
}
template <typename R2 = R,
std::enable_if_t<!std::is_void<R2>::value, int> = 0>
static int Outer(std::string* s,
std::unique_ptr<int> a,
std::unique_ptr<int> b) {
*s += "Outer";
*s += base::NumberToString(*a) + base::NumberToString(*b);
return *a + *b;
}
template <typename R2 = R,
std::enable_if_t<!std::is_void<R2>::value, int> = 0>
static int Outer(std::string* s, int a, int b) {
*s += "Outer";
*s += base::NumberToString(a) + base::NumberToString(b);
return a + b;
}
template <typename R2 = R,
std::enable_if_t<!std::is_void<R2>::value, int> = 0>
static int Outer(std::string* s) {
*s += "Outer";
*s += "None";
return 99;
}
template <typename R2 = R, std::enable_if_t<std::is_void<R2>::value, int> = 0>
static void Outer(std::string* s,
std::unique_ptr<int> a,
std::unique_ptr<int> b) {
*s += "Outer";
*s += base::NumberToString(*a) + base::NumberToString(*b);
}
template <typename R2 = R, std::enable_if_t<std::is_void<R2>::value, int> = 0>
static void Outer(std::string* s, int a, int b) {
*s += "Outer";
*s += base::NumberToString(a) + base::NumberToString(b);
}
template <typename R2 = R, std::enable_if_t<std::is_void<R2>::value, int> = 0>
static void Outer(std::string* s) {
*s += "Outer";
*s += "None";
}
template <typename OuterR,
typename InnerR,
std::enable_if_t<!std::is_void<OuterR>::value, int> = 0,
std::enable_if_t<!std::is_void<InnerR>::value, int> = 0>
static int Inner(std::string* s, OuterR a) {
static_assert(std::is_same<InnerR, int>::value, "Use int return type");
*s += "Inner";
*s += base::NumberToString(a);
return a;
}
template <typename OuterR,
typename InnerR,
std::enable_if_t<std::is_void<OuterR>::value, int> = 0,
std::enable_if_t<!std::is_void<InnerR>::value, int> = 0>
static int Inner(std::string* s) {
static_assert(std::is_same<InnerR, int>::value, "Use int return type");
*s += "Inner";
*s += "None";
return 99;
}
template <typename OuterR,
typename InnerR,
std::enable_if_t<!std::is_void<OuterR>::value, int> = 0,
std::enable_if_t<std::is_void<InnerR>::value, int> = 0>
static void Inner(std::string* s, OuterR a) {
*s += "Inner";
*s += base::NumberToString(a);
}
template <typename OuterR,
typename InnerR,
std::enable_if_t<std::is_void<OuterR>::value, int> = 0,
std::enable_if_t<std::is_void<InnerR>::value, int> = 0>
static void Inner(std::string* s) {
*s += "Inner";
*s += "None";
}
};
template <typename R, typename ThenR = void, typename... Args>
using CallbackThenOnceTest = CallbackThenTest<true, R, ThenR, Args...>;
template <typename R, typename ThenR = void, typename... Args>
using CallbackThenRepeatingTest = CallbackThenTest<false, R, ThenR, Args...>;
TEST_F(CallbackTest, ThenOnce) {
std::string s;
// Void return from outer + void return from Then().
{
using VoidReturnWithoutArgs = void();
using ThenReturn = void;
using Test = CallbackThenOnceTest<VoidReturnWithoutArgs, ThenReturn>;
Test::GetOuter(s).Then(Test::GetInner(s)).Run();
EXPECT_EQ(s, "OuterNoneInnerNone");
}
{
using VoidReturnWithArgs = void(int, int);
using ThenReturn = void;
using Test = CallbackThenOnceTest<VoidReturnWithArgs, ThenReturn>;
Test::GetOuter(s).Then(Test::GetInner(s)).Run(1, 2);
EXPECT_EQ(s, "Outer12InnerNone");
}
{
using VoidReturnWithMoveOnlyArgs =
void(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = void;
using Test = CallbackThenOnceTest<VoidReturnWithMoveOnlyArgs, ThenReturn>;
Test::GetOuter(s)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2));
EXPECT_EQ(s, "Outer12InnerNone");
}
// Void return from outer + non-void return from Then().
{
using VoidReturnWithoutArgs = void();
using ThenReturn = int;
using Test = CallbackThenOnceTest<VoidReturnWithoutArgs, ThenReturn>;
EXPECT_EQ(99, Test::GetOuter(s).Then(Test::GetInner(s)).Run());
EXPECT_EQ(s, "OuterNoneInnerNone");
}
{
using VoidReturnWithArgs = void(int, int);
using ThenReturn = int;
using Test = CallbackThenOnceTest<VoidReturnWithArgs, ThenReturn>;
EXPECT_EQ(99, Test::GetOuter(s).Then(Test::GetInner(s)).Run(1, 2));
EXPECT_EQ(s, "Outer12InnerNone");
}
{
using VoidReturnWithMoveOnlyArgs =
void(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = int;
using Test = CallbackThenOnceTest<VoidReturnWithMoveOnlyArgs, ThenReturn>;
EXPECT_EQ(99, Test::GetOuter(s)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2)));
EXPECT_EQ(s, "Outer12InnerNone");
}
// Non-void return from outer + void return from Then().
{
using NonVoidReturnWithoutArgs = int();
using ThenReturn = void;
using Test = CallbackThenOnceTest<NonVoidReturnWithoutArgs, ThenReturn>;
Test::GetOuter(s).Then(Test::GetInner(s)).Run();
EXPECT_EQ(s, "OuterNoneInner99");
}
{
using NonVoidReturnWithArgs = int(int, int);
using ThenReturn = void;
using Test = CallbackThenOnceTest<NonVoidReturnWithArgs, ThenReturn>;
Test::GetOuter(s).Then(Test::GetInner(s)).Run(1, 2);
EXPECT_EQ(s, "Outer12Inner3");
}
{
using NonVoidReturnWithMoveOnlyArgs =
int(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = void;
using Test =
CallbackThenOnceTest<NonVoidReturnWithMoveOnlyArgs, ThenReturn>;
Test::GetOuter(s)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2));
EXPECT_EQ(s, "Outer12Inner3");
}
// Non-void return from outer + non-void return from Then().
{
using NonVoidReturnWithoutArgs = int();
using ThenReturn = int;
using Test = CallbackThenOnceTest<NonVoidReturnWithoutArgs, ThenReturn>;
EXPECT_EQ(99, Test::GetOuter(s).Then(Test::GetInner(s)).Run());
EXPECT_EQ(s, "OuterNoneInner99");
}
{
using NonVoidReturnWithArgs = int(int, int);
using ThenReturn = int;
using Test = CallbackThenOnceTest<NonVoidReturnWithArgs, ThenReturn>;
EXPECT_EQ(3, Test::GetOuter(s).Then(Test::GetInner(s)).Run(1, 2));
EXPECT_EQ(s, "Outer12Inner3");
}
{
using NonVoidReturnWithMoveOnlyArgs =
int(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = int;
using Test =
CallbackThenOnceTest<NonVoidReturnWithMoveOnlyArgs, ThenReturn>;
EXPECT_EQ(3, Test::GetOuter(s)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2)));
EXPECT_EQ(s, "Outer12Inner3");
}
}
TEST_F(CallbackTest, ThenRepeating) {
std::string s;
// Void return from outer + void return from Then().
{
using VoidReturnWithoutArgs = void();
using ThenReturn = void;
using Test = CallbackThenRepeatingTest<VoidReturnWithoutArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
outer.Then(Test::GetInner(s)).Run();
EXPECT_EQ(s, "OuterNoneInnerNone");
std::move(outer).Then(Test::GetInner(s)).Run();
EXPECT_EQ(s, "OuterNoneInnerNoneOuterNoneInnerNone");
}
{
using VoidReturnWithArgs = void(int, int);
using ThenReturn = void;
using Test = CallbackThenRepeatingTest<VoidReturnWithArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
outer.Then(Test::GetInner(s)).Run(1, 2);
EXPECT_EQ(s, "Outer12InnerNone");
std::move(outer).Then(Test::GetInner(s)).Run(1, 2);
EXPECT_EQ(s, "Outer12InnerNoneOuter12InnerNone");
}
{
using VoidReturnWithMoveOnlyArgs =
void(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = void;
using Test =
CallbackThenRepeatingTest<VoidReturnWithMoveOnlyArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
outer.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2));
EXPECT_EQ(s, "Outer12InnerNone");
std::move(outer)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2));
EXPECT_EQ(s, "Outer12InnerNoneOuter12InnerNone");
}
// Void return from outer + non-void return from Then().
{
using VoidReturnWithoutArgs = void();
using ThenReturn = int;
using Test = CallbackThenRepeatingTest<VoidReturnWithoutArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
EXPECT_EQ(99, outer.Then(Test::GetInner(s)).Run());
EXPECT_EQ(s, "OuterNoneInnerNone");
EXPECT_EQ(99, std::move(outer).Then(Test::GetInner(s)).Run());
EXPECT_EQ(s, "OuterNoneInnerNoneOuterNoneInnerNone");
}
{
using VoidReturnWithArgs = void(int, int);
using ThenReturn = int;
using Test = CallbackThenRepeatingTest<VoidReturnWithArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
EXPECT_EQ(99, outer.Then(Test::GetInner(s)).Run(1, 2));
EXPECT_EQ(s, "Outer12InnerNone");
EXPECT_EQ(99, std::move(outer).Then(Test::GetInner(s)).Run(1, 2));
EXPECT_EQ(s, "Outer12InnerNoneOuter12InnerNone");
}
{
using VoidReturnWithMoveOnlyArgs =
void(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = int;
using Test =
CallbackThenRepeatingTest<VoidReturnWithMoveOnlyArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
EXPECT_EQ(99, outer.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2)));
EXPECT_EQ(s, "Outer12InnerNone");
EXPECT_EQ(99, std::move(outer)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2)));
EXPECT_EQ(s, "Outer12InnerNoneOuter12InnerNone");
}
// Non-void return from outer + void return from Then().
{
using NonVoidReturnWithoutArgs = int();
using ThenReturn = void;
using Test =
CallbackThenRepeatingTest<NonVoidReturnWithoutArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
outer.Then(Test::GetInner(s)).Run();
EXPECT_EQ(s, "OuterNoneInner99");
std::move(outer).Then(Test::GetInner(s)).Run();
EXPECT_EQ(s, "OuterNoneInner99OuterNoneInner99");
}
{
using NonVoidReturnWithArgs = int(int, int);
using ThenReturn = void;
using Test = CallbackThenRepeatingTest<NonVoidReturnWithArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
outer.Then(Test::GetInner(s)).Run(1, 2);
EXPECT_EQ(s, "Outer12Inner3");
std::move(outer).Then(Test::GetInner(s)).Run(1, 2);
EXPECT_EQ(s, "Outer12Inner3Outer12Inner3");
}
{
using NonVoidReturnWithMoveOnlyArgs =
int(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = void;
using Test =
CallbackThenRepeatingTest<NonVoidReturnWithMoveOnlyArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
outer.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2));
EXPECT_EQ(s, "Outer12Inner3");
std::move(outer)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2));
EXPECT_EQ(s, "Outer12Inner3Outer12Inner3");
}
// Non-void return from outer + non-void return from Then().
{
using NonVoidReturnWithoutArgs = int();
using ThenReturn = int;
using Test =
CallbackThenRepeatingTest<NonVoidReturnWithoutArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
EXPECT_EQ(99, outer.Then(Test::GetInner(s)).Run());
EXPECT_EQ(s, "OuterNoneInner99");
EXPECT_EQ(99, std::move(outer).Then(Test::GetInner(s)).Run());
EXPECT_EQ(s, "OuterNoneInner99OuterNoneInner99");
}
{
using NonVoidReturnWithArgs = int(int, int);
using ThenReturn = int;
using Test = CallbackThenRepeatingTest<NonVoidReturnWithArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
EXPECT_EQ(3, outer.Then(Test::GetInner(s)).Run(1, 2));
EXPECT_EQ(s, "Outer12Inner3");
EXPECT_EQ(3, std::move(outer).Then(Test::GetInner(s)).Run(1, 2));
EXPECT_EQ(s, "Outer12Inner3Outer12Inner3");
}
{
using NonVoidReturnWithMoveOnlyArgs =
int(std::unique_ptr<int>, std::unique_ptr<int>);
using ThenReturn = int;
using Test =
CallbackThenRepeatingTest<NonVoidReturnWithMoveOnlyArgs, ThenReturn>;
auto outer = Test::GetOuter(s);
EXPECT_EQ(3, outer.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2)));
EXPECT_EQ(s, "Outer12Inner3");
EXPECT_EQ(3, std::move(outer)
.Then(Test::GetInner(s))
.Run(std::make_unique<int>(1), std::make_unique<int>(2)));
EXPECT_EQ(s, "Outer12Inner3Outer12Inner3");
}
}
// WeakPtr detection in BindRepeating() requires a method, not just any // WeakPtr detection in BindRepeating() requires a method, not just any
// function. // function.
class ClassWithAMethod { class ClassWithAMethod {
......
...@@ -49,6 +49,96 @@ void WontCompile() { ...@@ -49,6 +49,96 @@ void WontCompile() {
cb_a = cb_b; cb_a = cb_b;
} }
#elif defined(NCTEST_ONCE_THEN_MISMATCH) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `int` to `float`.
void WontCompile() {
OnceCallback<int()> original;
OnceCallback<void(float)> then;
std::move(original).Then(std::move(then));
}
#elif defined(NCTEST_ONCE_THEN_MISMATCH_VOID_RESULT) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `void` to `float`.
void WontCompile() {
OnceCallback<void()> original;
OnceCallback<void(float)> then;
std::move(original).Then(std::move(then));
}
#elif defined(NCTEST_ONCE_THEN_MISMATCH_VOID_PARAM) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `int` to `void`.
void WontCompile() {
OnceCallback<int()> original;
OnceCallback<void()> then;
std::move(original).Then(std::move(then));
}
#elif defined(NCTEST_REPEATINGRVALUE_THEN_MISMATCH) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `int` to `float`.
void WontCompile() {
RepeatingCallback<int()> original;
RepeatingCallback<void(float)> then;
std::move(original).Then(std::move(then));
}
#elif defined(NCTEST_REPEATINGRVALUE_THEN_MISMATCH_VOID_RESULT) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `void` to `float`.
void WontCompile() {
RepeatingCallback<void()> original;
RepeatingCallback<void(float)> then;
std::move(original).Then(std::move(then));
}
#elif defined(NCTEST_REPEATINGRVALUE_THEN_MISMATCH_VOID_PARAM) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `int` to `void`.
void WontCompile() {
RepeatingCallback<int()> original;
RepeatingCallback<void()> then;
std::move(original).Then(std::move(then));
}
#elif defined(NCTEST_REPEATINGLVALUE_THEN_MISMATCH) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `int` to `float`.
void WontCompile() {
RepeatingCallback<int()> original;
RepeatingCallback<void(float)> then;
original.Then(then);
}
#elif defined(NCTEST_REPEATINGLVALUE_THEN_MISMATCH_VOID_RESULT) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `void` to `float`.
void WontCompile() {
RepeatingCallback<void()> original;
RepeatingCallback<void(float)> then;
original.Then(then);
}
#elif defined(NCTEST_REPEATINGLVALUE_THEN_MISMATCH_VOID_PARAM) // [r"fatal error: static_assert failed due to requirement '.*' \"The |then| callback must accept the return value from the outer callback as its only parameter.\""]
// Calling Then() with a callback that can't receive the original
// callback's return type. Here we would pass `int` to `void`.
void WontCompile() {
RepeatingCallback<int()> original;
RepeatingCallback<void()> then;
original.Then(then);
}
#endif #endif
} // namespace base } // namespace base
...@@ -84,6 +84,85 @@ moved-from `base::{Once,Repeating}Callback` becomes null, as if its `Reset()` ...@@ -84,6 +84,85 @@ moved-from `base::{Once,Repeating}Callback` becomes null, as if its `Reset()`
method had been called. Afterward, its `is_null()` method will return true and method had been called. Afterward, its `is_null()` method will return true and
its `operator bool()` will return false. its `operator bool()` will return false.
### Chaining callbacks
When you have 2 callbacks that you wish to run in sequence, they can be joined
together into a single callback through the use of `Then()`.
Calling `Then()` on a `base::OnceCallback` joins a second callback that will be
run together with, but after, the first callback. The return value from the
first callback is passed along to the second, and the return value from the
second callback is returned at the end. More concretely, calling `a.Then(b)`
produces a new `base::OnceCallback` that will run `b(a());`, returning the
result from `b`.
This example uses `Then()` to join 2 `base::OnceCallback`s together:
```cpp
int Floor(float f) { return std::floor(f); }
std::string IntToString(int i) { return base::NumberToString(i); }
base::OnceCallback<int(float)> first = base::BindOnce(&Floor);
base::OnceCallback<std::string(int)> second = base::BindOnce(&IntToString);
// This will run |first|, run and pass the result to |second|, then return
// the result from |second|.
std::string r = std::move(first).Then(std::move(second)).Run(3.5f);
// |r| will be "3". |first| and |second| are now both null, as they were
// consumed to perform the join operation.
```
Similarly, `Then()` also works with `base::RepeatingCallback`; however, the
joined callback must also be a `base::RepeatingCallback` to ensure the resulting
callback can be invoked multiple times.
This example uses `Then()` to join 2 `base::RepeatingCallback`s together:
```cpp
int Floor(float f) { return std::floor(f); }
std::string IntToString(int i) { return base::NumberToString(i); }
base::RepeatingCallback<int(float)> first = base::BindRepeating(&Floor);
base::RepeatingCallback<std::string(int)> second = base::BindRepeating(&IntToString);
// This creates a RepeatingCallback that will run |first|, run and pass the
// result to |second|, then return the result from |second|.
base::RepeatingCallback<std::string(float)> joined =
std::move(first).Then(std::move(second));
// |first| and |second| are now both null, as they were consumed to perform
// the join operation.
// This runs the functor that was originally bound to |first|, then |second|.
std::string r = joined.Run(3.5);
// |r| will be "3".
// It's valid to call it multiple times since all callbacks involved are
// base::RepeatingCallbacks.
r = joined.Run(2.5);
// |r| is set to "2".
```
In the above example, casting the `base::RepeatingCallback` to an r-value with
`std::move()` causes `Then()` to destroy the original callback, in the same way
that occurs for joining `base::OnceCallback`s. However since a
`base::RepeatingCallback` can be run multiple times, it can be joined
non-destructively as well.
```cpp
int Floor(float f) { return std::floor(f); }
std::string IntToString(int i) { return base::NumberToString(i); }
base::RepeatingCallback<int(float)> first = base::BindRepeating(&Floor);
base::RepeatingCallback<std::string(int)> second = base::BindRepeating(&IntToString);
// This creates a RepeatingCallback that will run |first|, run and pass the
// result to |second|, then return the result from |second|.
std::string r = first.Then(second).Run(3.5f);
// |r| will be 3, and |first| and |second| are still valid to use.
// Runs Floor().
int i = first.Run(5.5);
// Runs IntToString().
std::string s = second.Run(9);
```
## Quick reference for basic stuff ## Quick reference for basic stuff
### Binding A Bare Function ### Binding A Bare Function
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment