Callbacks
Function objects
The libbase
library provides two implementations of templated functions
objects:
These two types represent generic functors that take Arguments...
arguments as an input and return result of type Result
(or nothing, if
Result
is void
). The main differences between them are whether they are
copyable and at most how many times they can be run/called.
Type |
Copyable |
Moveable |
Max runs |
---|---|---|---|
✕ |
✓ |
1 |
|
✓ |
✓ |
∞ |
Callbacks that don’t take any arguments and return nothing are called closures. There are two handy aliases defined for them:
Example
base::OnceCallback<int(float, std::string)>
refers to callbacks that can be run at most once, take two arguments to run: a float and a string, and the result of running them will be of the integer type.
base::RepeatingCallback<void()>
(orbase::RepeatingClosure
)refers to callbacks that can be run any number of times, take no arguments and return nothing from each run.
Note
These types act similarly to std::function<...>
[1] template
class from the standard library, except that they provide additional
functionality and better integration with the rest of libbase
components.
Important
Prefer using OnceCallback<...>
when possible, as it provides clearer ownership and
lifetime semantics.
Hint
RepeatingCallback<...>
callbacks are implicitly convertible to OnceCallback<...>
counterparts.
Caution
When using the original implementations from Chromium’s //base
module,
copies of the same RepeatingCallback<...>
callbacks share any bound state. In libbase
,
however, they do not and the state is copied whenever the callback is copied.
Your application should not depend on it as this may change in the future
versions of libbase
library.
Running callbacks
To run (execute/call) OnceCallback<...>
you need to std::move()
the object to
obtain rvalue-reference to it. This is done to signal to the readers of code
that this callback will be reset (“moved-away”) after running it.
Example - running OnceCallback<...>
base::OnceCallback<...> my_callback = /* ... */;
auto result = std::move(my_callback).Run(/* arguments */);
CHECK(!my_callback);
RepeatingCallback<...>
can be run both with lvalue-reference and rvalue-reference - the
former will keep the callback unmodified, while the latter will reset it.
Example - running RepeatingCallback<...>
base::RepeatingCallback<...(...)> my_callback = /* ... */;
auto result_1 = my_callback.Run(/* arguments */);
CHECK(my_callback);
auto result_2 = std::move(my_callback).Run(/* arguments */);
CHECK(!my_callback);
Binding callbacks
To create a callback you need to bind a functor (function, member function, captureless lambda or another callback) and - optionally - perform a partial application of some arguments.
libbase
provides two functions that allow you to create a callback of a
given type and perform partial application:
base::BindOnce()
- binds functor and returnsOnceCallback<...>
.base::BindRepeating()
- binds functor and returnsRepeatingCallback<...>
.
All passed arguments are then copied (or moved) into the callback state and will be used when the callback will be run.
Note
These functions are similar to std::bind_front()
[2] from
the standard library.
Attention
You cannot bind move-only types with a base::BindRepeating()
to
RepeatingCallback<...>
if they will be passed as an argument taken by value. This is
because RepeatingCallback<...>
are allowed to be copied and execute any number of times,
while the bound argument would have to be moved-into the bound function.
Example - base::BindOnce()
1 #include "base/bind.h"
2 #include "base/callback.h"
3
4 int GetNext(int x) {
5 return x + 1;
6 }
7
8 int main() {
9 base::OnceCallback<int(int)> cb_1 = base::BindOnce(&GetNext);
10 CHECK(std::move(cb_1).Run(5) == 6);
11 CHECK(!cb_1); // `cb_1` can be executed only once
12
13 base::OnceCallback<int()> cb_2 = base::BindOnce(&GetNext, 3);
14 CHECK(std::move(cb_1).Run() == 4);
15 CHECK(!cb_2); // `cb_2` can be executed only once too
16
17 return 0;
18 }
Example - base::BindRepeating()
1 #include "base/bind.h"
2 #include "base/callback.h"
3
4 int IncrementBy(int increment, int value) {
5 return value + increment;
6 }
7
8 int main() {
9 base::RepeatingCallback<int(int)> get_next =
10 base::BindOnce(&IncrementBy, 1);
11
12 CHECK(get_next.Run(5) == 6);
13 CHECK(get_next.Run(2) == 3);
14 CHECK(get_next); // `get_next` is still valid
15
16 return 0;
17 }
Binding adapters
Member functions
libbase
library forbids binding member functions directly to a raw
pointers to the object of that class as this is often source of many
lifetime-related bugs. To bind a callback pointing to a member function, you
need to use one of the provided adapters.
Weak pointers
If you do not want to use shared_ptr
or possibly extend the lifetime of your
object, you may use base::WeakPtr
. These weak pointers allow you to
check if the pointed-to object is still alive and - if so - access it safely.
When a callback to a member function bound with a base::WeakPtr
is
run, the member function will only be executed if the original object pointed to
by the base::WeakPtr
is still alive, otherwise the call will be ignored
and there will be no attempt to access the (now probably destroyed) original
object. There are no special/extra adapter for weak pointers - the
base::WeakPtr
is itself recognized as such and allowed to be used.
Caution
Using weak pointers is a bit more complicated than as described here. See Weak pointers page for more details.
Attention
It is forbidden to bind a member function, that has a non-void return type, to a weak pointer.
Example - binding member function to base::WeakPtr<>
#include <iostream>
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
struct Foo {
void bar() { std::cout << "Hello World!\n"; }
};
int main() {
std::unique_ptr<Foo> foo = std::make_unique<Foo>();
base::WeakPtr<Foo> weak_foo = /* obtain the weak pointer */;
auto callback = base::BindRepeating(&Foo::bar, weak_foo);
callback.Run(); // will print "Hello World!";
foo.reset(); // this invalidates all existing weak pointers to `foo`
callback.Run(); // NO-OP, will do nothing
// note that `callback` is NOT reset/empty at this point
return 0;
}
Manual lifetime management
In scenarios where you are sure that object to which the member function will be
bound will outlive the callback, you may disable the lifetime management logic
and force the callback to bind to - essentially - a raw pointer to that object.
This can be done with base::Unretained()
adapter.
Caution
While this construct reduces the overhead of the callback execution, it is often a source of lifetime-related bugs. Be careful when using it!
Example - binding member function to base::Unretained()
/raw pointer
#include <iostream>
#include "base/bind.h"
#include "base/callback.h"
struct Foo {
void bar() { std::cout << "Hello World!\n"; }
};
int main() {
std::unique_ptr<Foo> foo = std::make_unique<Foo>();
auto callback = base::BindRepeating(&Foo::bar, base::Unretained(foo.get());
callback.Run(); // will print "Hello World!";
foo.reset();
// WARNING:
// calling `callback.Run()` now would invoke Undefined Behavior
// (use-after-free)
return 0;
}
Ignore result
In some scenarios, it might be required to drop the result from a callback (e.g.
to match some APIs that don’t expect any return values). In such cases, it is
possible to do so with the base::IgnoreResult()
adapter.
Example - binding function with base::IgnoreResult()
adapter
#include <iostream>
#include <string>
#include "base/bind.h"
#include "base/callback.h"
int PrintAndReturnLength(const std::string& text) {
std::cout << text << std::endl;
return text.length();
}
void DoSomething(base::RepeatingCallback<void(const std::string&)> print_cb);
int main() {
base::RepeatingCallback<void(const std::string&)> print_cb =
base::BindRepeating(base::IgnoreResult(&PrintAndReturnLength));
DoSomething(std::move(print_cb);
return 0;
}
Binding arguments into the callback
You can bind arguments into a callback such that it will own them and pass them to the function (via pointer or reference).
Example - binding arguments with base::Owned()
and base::OwnedRef()
#include <iostream>
#include <string>
#include "base/bind.h"
#include "base/callback.h"
void PrintByPtr(int* value) {
std::cout << *value << std::endl;
}
void PrintByRef(int& value) {
std::cout << value << std::endl;
}
int main() {
auto cb_ptr =
base::BindRepeating(&PrintByPtr, base::Owned(std::make_unique<int>(5)));
cb_ptr.Run(); // will print `6`
auto cb_ref = base::BindRepeating(&PrintByRef, base::OwnedRef(1));
cb_ref.Run(); // will print `1`
return 0;
}
Chaining callbacks
If you need to run multiple callbacks in a sequence, possibly passing result
from the previous callback as an input to the next one, you can use
.Then(/* next_callback */)
method to obtain a new callback composed from the
provided two provided ones.
auto chained_cb = first_cb.Then(second_cb); // Running `chained_cb` is equivalent to calling: // - `first_cb.Run(); second_cb.Run();` - if `first_cb` returns nothing // - `second_cb.Run(first_cb.Run());` - if `first_cb` returns a result
Normal lvalue/rvalue-reference rules applies - OnceCallback<...>
must be moved while
RepeatingCallback<...>
can be either copied or moved into the chained callback.
When chaining OnceCallback<...>
with any callback, the result will be of OnceCallback<...>
type.
RepeatingCallback<...>
callbacks can be chained only with other RepeatingCallback<...>
callbacks.
Example - chaining callbacks
#include <string>
#include "base/bind.h"
#include "base/callback.h"
int GetNext(int value) {
return value + 1;
}
std::string AsString(int value) {
return std::to_string(value);
}
int main() {
base::RepeatingCallback<std::string(int)> get_next_as_string =
base::BindRepeating(&GetNext).Then(base::BindRepeating(&AsString));
CHECK(get_next_as_string.Run(5) == "6");
return 0;
}
Chaining callbacks across different task runners
Caution
Before reading this section, familiarize yourself with threads, sequences and task runners on the Threads and sequences page.
There may be a situation where you will have to pass a callback to receive a notification of some action which will have to be processed on a specific thread or sequence, and you won’t be sure on which thread/sequence it will be invoked.
In cases like this, you can bind an existing callback to a post-task operation.
This will result in creating a new callback that - when executed - will
post-task the original callback to the target task runner. This is possible with
base::BindPostTask()
.
Example - base::BindPostTask()
1 #include "base/bind.h"
2 #include "base/callback.h"
3
4 // No guarantee on which thread/sequence the `on_done` callback will be
5 // called.
6 void DoSomeWorkAsync(base::OnceClosure on_done);
7
8 // Our code
9 void WorkManager::StartSomeWork() {
10 auto on_done_callback = base::BindOnce(&WorkManager::WorkDone, weak_this_);
11 DoSomeWorkAsync(
12 base::BindPostTask(current_task_runner_, std::move(on_done_callback)));
13 }
14
15 void WorkManager::WorkDone() {
16 // The check below is safe to perform
17 DCHECK(current_task_runner_->RunsTasksInCurrentSequence());
18
19 // ...
20 }
Splitting a OnceCallback
Sometimes you may have to bridge two APIs: one that takes two different
callbacks where exactly one of them will be called - one on success and the
other on failure, while on the other hand have single OnceCallback<...>
that takes
a boolean or an enum and has to be called to notify if the operation succeded or
failed.
Due to OnceCallback<...>
not being copyable, you cannot simply create two copies and bind
one with success value while the other with failure value.
There is however a tool to help you with this scenario:
base::SplitOnceCallback()
function. It takes a single OnceCallback<...>
callback
and returns a pair of new OnceCallback<...>
callbacks. Running either of the new
callbacks will run the original one. Running the other will result in trggering
a CHECK()
and crashing.
Example - base::SplitOnceCallback()
1 #include "base/bind.h"
2 #include "base/callback.h"
3
4 // Assumed API that we cannot change
5 void DoSomeWork(base::OnceClosure on_success_callback,
6 base::OnceClosure on_failure_callback);
7
8 // Our implementation that has to adapt to external APIs
9 void DoSomeWorkAndReport(base::OnceCallback<void(bool)> on_done_callback) {
10 auto callback_pair = base::SplitOnceCallback(std::move(on_done_callback));
11 DoSomeWork(base::BindOnce(std::move(callback_pair.first), true),
12 base::BindOnce(std::move(callback_pair.first), false));
13 }
Caution
Be careful not to overuse this functionality as it can complicate the logic and control flow of your application.
BarrierCallback and BarrierClosure
You might be required to collect some data from several places asynchronously,
and once all the data is collected, do something with it only then. To
facilitate this scenario, libbase
has base::BarrierCallback()
function.
To create a BarrierCallback<T>
you need to pass two arguments:
size_t required_run_count
- number of times the callback must be run (and how many data chunks need to be collected).base::OnceCallback<void(Container<T>)> done_callback
- a callback that will be invoked with the collected data chunks.
This will create a new RepeatingCallback<...>
which will take argument of T
type. Then,
after it (or its copies) will be invoked exactly required_run_count
times,
the done_callback
will be invoked with all the collected data chunks.
If you don’t need to collect any data, you can also use
base::BarrierClosure()
method.
Tip
Resulting barrier callback of RepeatingCallback<...>
is thread-safe, meaning that you can
safely copy it to different threads and run it without any additional
synchronization between them. The done_callback
will be run on the same
thread/sequence as the final call to the barrier callback.
Example - base::BarrierClosure()
1 #include <iostream>
2
3 #include "base/barrier_closure.h"
4 #include "base/bind.h"
5
6 int main() {
7 base::RepeatingClosure barrier_cb = base::BarrierClosure(
8 3, base::BindOnce([]() { std::cout << "Hello World!\n"; }));
9
10 barrier_cb.run(); // nothing
11 barrier_cb.run(); // nothing
12 barrier_cb.run(); // prints "Hello World!" on the screen
13 // `barrier_cb` must NOT be called any more!
14
15 return 0;
16 }
Hint
You can find more details about callbacks and binding them here .