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
......
This diff is collapsed.
...@@ -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