Chromium文档:Callback and Bind(),基于75.0.3770.4 (1)

简介

模板化的base::Callback<>类是一个广义函数对象。与base/bind.h中的base::Bind()函数一起,它们为执行函数的Partial application提供了一种类型安全的方法。

Partial application(或“currying”)是一个绑定函数参数的子集,以生成另一个使用较少参数的函数的过程。这可以用来传递延迟执行的单元,就像在其他语言中使用词法闭包一样。例如,在Chromium代码中使用它在不同的MessageLoop上调度任务。

没有unbound输入参数的回调(base::Callback<void()>)称为base::Closure。请注意,这与其他语言所指的闭包不同——它不保留对其封闭环境的引用。


OnceCallback<>和RepeatingCallback<>

base::OnceCallback<>和base::RepeatingCallback<>是下一代回调类,正在开发中。

base::OnceCallback<>由base::BindOnce()创建。这是一个只移动类型的回调变量,只能运行一次。默认情况下,这会将绑定参数从其内部存储移出到绑定函数,因此更易于与可移动类型一起使用。这应该是首选的回调类型:因为回调的生存期是明确的,所以推理线程之间传递的回调何时被销毁比较简单。

base::RepeatingCallback<>由base::BindRepeating()创建。这是一个可复制的回调变量,可以多次运行。它使用内部引用计数来使副本变得便宜。然而,由于所有权是共享的,所以很难推断回调和绑定状态何时被破坏,特别是当回调在线程之间传递时。

旧版本的base::Callback<>现在的别名为base::RepeatingCallback<>。在新代码中,尽可能选择base::OnceCallback<>,无法使用时才应使用base::RepeatingCallback<>。迁移完成后,base::RepeatingCallback<>别名将被删除,base::OnceCallback<>将重命名为base::Callback<>,以强调首选。

base::RepeatingCallback<>可以通过隐式转换为base::OnceCallback<>。



内存管理和传递

如果所有权转移,则按值传递base::Callback对象;否则,按常量引用传递。


cpp

// |Foo| 只引用 |cb| 但不储存或消耗它。

bool Foo(const base::OnceCallback<void(int)>& cb) {

  return cb.is_null();

}


// |Bar| 获取 |cb| 的控制权,存储 |cb| 到 |g_cb|。

base::OnceCallback<void(int)> g_cb;

void Bar(base::OnceCallback<void(int)> cb) {

  g_cb = std::move(cb);

}


// |Baz| 获取 |cb| 的控制权并通过 Run() 来消耗 |cb|。

void Baz(base::OnceCallback<void(int)> cb) {

  std::move(cb).Run(42);

}


// |Qux| 获取 |cb| 的控制权并转移控制权给 PostTask(),

// PostTask也获取 |cb| 的控制权。

void Qux(base::OnceCallback<void(int)> cb) {

  PostTask(FROM_HERE,

           base::BindOnce(std::move(cb), 42));

}



当你将base::Callback对象传递给函数参数时,如果不需要保留对它的引用,请使用std::move(),否则直接传递该对象。


当函数需要独占所有权并且你没有按move传递回调时,你可能会看到编译错误。注意,被移动的base::Callback变为NULL,就好像调用了它的reset()方法,并且它的is_null()方法将返回true。



基础资料快速参考

绑定裸函数


```cpp

int Return5() { return 5; }

base::OnceCallback<int()> func_cb = base::BindOnce(&Return5);

LOG(INFO) << std::move(func_cb).Run();  // Prints 5.

```


```cpp

int Return5() { return 5; }

base::RepeatingCallback<int()> func_cb = base::BindRepeating(&Return5);

LOG(INFO) << func_cb.Run();  // Prints 5.

```

绑定无捕获Lambda

```cpp

base::Callback<int()> lambda_cb = base::Bind([] { return 4; });

LOG(INFO) << lambda_cb.Run();  // Print 4.


base::OnceCallback<int()> lambda_cb2 = base::BindOnce([] { return 3; });

LOG(INFO) << std::move(lambda_cb2).Run();  // Print 3.

```


绑定有捕获的Lambda(测试中)

在编写测试时,捕获需要在回调中修改的参数通常很有用。


``` cpp

#include "base/test/bind_test_util.h"


int i = 2;

base::Callback<void()> lambda_cb = base::BindLambdaForTesting([&]() { i++; });

lambda_cb.Run();

LOG(INFO) << i;  // Print 3;

```


绑定一个类方法

要绑定的第一个参数是要调用的成员函数,第二个参数是要调用它的对象。




```cpp

class Ref : public base::RefCountedThreadSafe<Ref> {

 public:

  int Foo() { return 3; }

};

scoped_refptr<Ref> ref = new Ref();

base::Callback<void()> ref_cb = base::Bind(&Ref::Foo, ref);

LOG(INFO) << ref_cb.Run();  // Prints out 3.

```


默认情况下,对象必须支持RefCounted,否则将出现编译器错误。如果要在线程之间传递,请确保它是RefCountedThreadSafe。如果不想使用引用计数,请参阅下面的“成员函数的高级绑定”。


运行回调

回调可以使用其`Run`方法运行,该方法与回调的模板参数具有相同的Signature。注意,base::OnceCallback::Run消耗回调对象,只能在回调rvalue上调用。



```cpp

void DoSomething(const base::Callback<void(int, std::string)>& callback) {

  callback.Run(5, "hello");

}


void DoSomethingOther(base::OnceCallback<void(int, std::string)> callback) {

  std::move(callback).Run(5, "hello");

}

```


可以多次运行RepeatingCallback(运行时不会删除或标记它们)。但是,这样就不能使用base::Passed了(见下文)。


```cpp

void DoSomething(const base::RepeatingCallback<double(double)>& callback) {

  double myresult = callback.Run(3.14159);

  myresult += callback.Run(2.71828);

}

```


如果运行回调可能导致其自身销毁(例如,如果回调接收者删除了回调是其成员的对象),则应该在可以安全调用回调之前移动回调。(请注意,这只是RepeatingCallback的问题,因为它总是必须移动OnceCallback才能执行。)



```cpp

void Foo::RunCallback() {

  std::move(&foo_deleter_callback_).Run();

}

```

创建不做任何事情的回调

有时,您需要一个在运行时什么都不做的回调(例如,不关心被通知某些类型的事件的测试代码)。传递一个默认构造的正确类型的回调可能很有诱惑力:



```cpp

using MyCallback = base::OnceCallback<void(bool arg)>;

void MyFunction(MyCallback callback) {

  std::move(callback).Run(true);  // Uh oh...

}

...

MyFunction(MyCallback());  // ...this will crash when Run()!

```




默认构造的回调为null,因此无法Run()。取而代之的是使用base::DoNothing():



```cpp

...

MyFunction(base::DoNothing());  // Can be Run(), will no-op

```



返回void的任何OnceCallback或RepeatingCallback都可以传递base::DoNothing()。

在实现方面,base::DoNothing()实际上是一个从operator()产生回调的函数。这使得它在尝试将其他参数绑定到它时不可用。通常,将参数绑定到DoNothing()的唯一原因是管理对象生存期,在这种情况下,您应该努力使用DeleteSoon()、ReleaseSoon()或RefCountedDeleteOnSequence这样的词语。如果确实需要将参数绑定到DoNothing(),或者如果需要显式创建回调对象(因为通过operator()进行的隐式转换不会编译),则可以直接实例化:




```cpp

// Binds |foo_ptr| to a no-op OnceCallback takes a scoped_refptr<Foo>.

// ANTIPATTERN WARNING: This should likely be changed to ReleaseSoon()!

base::Bind(base::DoNothing::Once<scoped_refptr<Foo>>(), foo_ptr);

```