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

OnceCallback<...>

1

RepeatingCallback<...>

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()> (or base::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:

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.

Shared pointers

One of the ways you can bind a member function to a specific object is to pass a std::shared_ptr<> to that object wrapped in base::RetainedRef() adapter. This adapter binds the shared pointer within the callback state.

Warning

This means that the object’s lifetime will be extended with the lifetime of the callback and all of its copies!

Example - binding member function to std::shared_ptr<>

#include <iostream>
#include <memory>

#include "base/bind.h"
#include "base/callback.h"

struct Foo {
  void bar() { std::cout << "Hello World!\n"; }
};

int main() {
  std::shared_ptr<Foo> foo = std::make_shared<Foo>();
  auto callback = base::BindRepeating(&Foo::bar, base::RetainedRef(foo));
  callback.Run();  // will print "Hello World!";
  return 0;
}

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 .