传递 base::{Once,Repeating} 如果所有权转移,则按值回调对象;否则,通过 引用传递
//Foo只是指向cb,但是不存储,也不消耗它 bool Foo(const base::OnceCallback<void(int)>& cb) { return cb.is_null(); } //Bar取得cb所有权,g_cb存储了他 base::RepeatingCallback<void(int)> g_cb; void Bar(base::RepeatingCallback<void(int)> cb) { g_cb = std::move(cb); } //Baz 取地所有权, 消耗它通过执行Run() void Baz(base::OnceCallback<void(int)> cb) { std::move(cb).Run(42); } //PostTask取得所有权 void Qux(base::RepeatingCallback<void(int)> cb) { PostTask(FROM_HERE, base::BindOnce(cb, 42)); PostTask(FROM_HERE, base::BindOnce(std::move(cb), 43)); }
将 base::{Once,Repeating}Callback 对象传递给函数参数时,如果不需要保留对它的引用,请使用 std::move() ,否则直接传递对象。
当函数需要独占所有权并且您没有通过移动传递回调时,您可能会看到编译错误。
请注意,移出的 base::{Once,Repeating}Callback 变为 null,就好像它的 Reset() 方法已被调用。之后,它的 is_null() 方法将返回 true,其 operator bool() 将返回 false。
链接回调
当您希望按顺序运行 2 个回调时,可以通过使用 Then() 将它们连接在一起成为一个回调。在 base::OnceCallback 上调用 Then() 会加入第二个回调,
该回调将与第一个回调一起运行,但在第一个回调之后。第一个回调的返回值传递给第二个,第二个回调的返回值在最后返回。更具体地说,
调用 a.Then(b) 会产生一个新的 base::OnceCallback,它将运行 b(a());,从 b 返回结果。
此示例使用 Then() 将 2 个 base::OnceCallbacks 连接在一起:
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);
这将运行|first|,运行并将结果传递给|second|,然后返回|second| 的结果。
std::string r = std::move(first).Then(std::move(second)).Run(3.5f);
|r|将是"3"。 |第一|和 |第二|现在都是空的,因为它们是用于执行连接操作。
同样, Then() 也适用于 base::RepeatingCallback;但是,加入的回调也必须是 base::RepeatingCallback 以确保可以多次调用生成的回调。
此示例使用 Then() 将 2 个 base::RepeatingCallbacks 连接在一起:
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); // 这会创建一个 RepeatingCallback,它将运行 |first|,运行并传递 // 结果到|second|,然后从|second| 返回结果。 base::RepeatingCallback<std::string(float)> joined = std::move(first).Then(std::move(second)); // //|第一|和 |第二|现在都为空,因为它们被消耗来执行连接操作。 //这会运行最初绑定到 |first| 的函子,然后是 |second| std::string r = joined.Run(3.5); // |r| will be "3". //多次调用它是有效的,因为所有涉及的回调都是base::RepeatingCallbacks。 r = joined.Run(2.5);
// |r| is set to "2".
在上面的示例中,使用 std::move() 将 base::RepeatingCallback 强制转换为 r 值会导致 Then() 破坏原始回调,与加入 base::OnceCallbacks 时发生的方式相同。 然而,由于 base::RepeatingCallback 可以运行多次,它也可以非破坏性地加入。
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); std::string r = first.Then(second).Run(3.5f); int i = first.Run(5.5); std::string s = second.Run(9);
如果第二个回调不想从第一个回调接收值,您可以使用 base::IgnoreResult 在运行两者之间删除返回值。
base::RepeatingCallback<int()> first = base::BindRepeating([](){ return 5; }); //不想要接收结果 base::RepeatingClosure second = base::BindRepeating([](){}); //这不会编译,因为 |second| 无法接收到|first|返回值 // first.Then(second).Run(); // 我们可以从 |first| 中删除结果 在第二次运行之前。 base::BindRepeating(base::IgnoreResult(first)).Then(second).Run(); // 这将有效地创建一个回调,当 Run() 将调用该回调 // `first(); second();` 而不是`second(first());`。
注意 |first| 的返回值 在上面的例子中会丢失,并且会在 |second| 之前被销毁 正在运行。 如果你想要 |first| 的返回值 在运行两个 |first| 后被保存并最终返回 和 |second|,
那么您将需要一个原语,例如 base::PassThrough CL 中的 base::PassThrough<T>() 助手。 如果这对您有帮助,请让 danakj@chromium.org 知道或 ping CL。
跨不同任务运行器链接回调
// 不同线程的任务运行器。 scoped_refptr<base::SequencedTaskRunner> other_task_runner = ...; //一个计算一些有趣结果的函数,除了它只能运行安全地来自 `other_task_runner` 而不是当前线程。 int ComputeResult(); base::OnceCallback<int()> compute_result_cb = base::BindOnce(&ComputeResult); // 当前线程的任务运行器。 scoped_refptr<base::SequencedTaskRunner> current_task_runner = base::SequencedTaskRunnerHandle::Get(); // 一个接受结果的函数,除了它只能从当前线程安全运行。 void ProvideResult(int result); base::OnceCallback<void(int)> provide_result_cb = base::BindOnce(&ProvideResult);
使用 Then() 直接加入 compute_result_cb 和 provide_result_cb 是不合适的。 ComputeResult() 和 ProvideResult() 将在不安全的同一线程上运行。 但是,base::BindPostTask() 可用于确保 provide_result_cb 将在 current_task_runner 上运行。
// 以下两条语句将任务发布到 `other_task_runner` 以运行`任务`。 这将在不同的线程上调用 ComputeResult() 以获取结果值然后将任务发布回 `current_task_runner` 以调用提供结果()和结果。 OnceClosure task = std::move(compute_result_cb) .Then(base::BindPostTask(current_task_runner, std::move(provide_result_cb))); other_task_runner->PostTask(FROM_HERE, std::move(task));
将 OnceCallback 一分为二
如果回调只运行一次,
但是需要对回调进行两个引用,使用 base::OnceCallback 可以比 base::RepeatingCallback 更清晰,从意图和语义的角度来看。 base::SplitOnceCallback() 接受一个 base::OnceCallback 并返回一对具有相同签名的回调。当运行返回的回调中的任何一个时,都会调用原始回调。 运行剩余的回调将导致崩溃。 这在将 base::OnceCallback 传递给可能拥有或不拥有回调所有权的函数时很有用。 例如,当对象创建可能失败时:
std::unique_ptr<FooTask> CreateFooTask(base::OnceClosure task) { std::pair<base::OnceClosure,base::OnceClosure> split = base::SplitOnceCallback(std::move(task)); std::unique_ptr<FooTask> foo = TryCreateFooTask(std::move(split.first)); if (foo) return foo; return CreateFallbackFooTask(std::move(split.second)); }
虽然最好使用单个回调来报告成功/失败,但某些 API 已经采用多个回调。 base::SplitOnceCallback() 可用于拆分完成回调并在这种情况下提供帮助:
using StatusCallback = base::OnceCallback<void(FooStatus)>; void DoOperation(StatusCallback done_cb) { std::pair<StatusCallback, StatusCallback> split = base::SplitOnceCallback(std::move(done_cb)); InnerWork(BindOnce(std::move(split.first), STATUS_OK), BindOnce(std::move(split.second), STATUS_ABORTED)); } void InnerWork(base::OnceClosure work_done_cb, base::OnceClosure work_aborted_cb);
基本资料的快速参考
绑定一个裸函数
int Return5() { return 5; } base::OnceCallback<int()> func_cb = base::BindOnce(&Return5); LOG(INFO) << std::move(func_cb).Run(); // Prints 5.
int Return5() { return 5; } base::RepeatingCallback<int()> func_cb = base::BindRepeating(&Return5); LOG(INFO) << func_cb.Run(); // Prints 5.
绑定无捕获的 Lambda
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. base::OnceCallback<int()> lambda_cb3 = base::BindOnce([] { return 2; }); base::OnceCallback<int(base::OnceCallback<int()>)> lambda_cb4 = base::BindOnce( [](base::OnceCallback<int()> callback) { return std::move(callback).Run(); }, std::move(lambda_cb3)); LOG(INFO) << std::move(lambda_cb4).Run(); // Print 2.
绑定一个捕获的 Lambda(测试)
#include "base/test/bind.h" int i = 2; base::Callback<void()> lambda_cb = base::BindLambdaForTesting([&]() { i++; }); lambda_cb.Run(); LOG(INFO) << i; // Print 3;
绑定一个类方法
bind 的第一个参数是要调用的成员函数,第二个参数是要调用它的对象。
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! 如果您不想使用引用计数,请参阅下面的“成员函数的高级绑定”。
运行回调
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/268194.html