ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BindOnce and callBack

2022-06-18 11:35:27  阅读:164  来源: 互联网

标签:std cb move BindOnce callBack second base first



传递 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! 如果您不想使用引用计数,请参阅下面的“成员函数的高级绑定”。

运行回调

 
 
 

标签:std,cb,move,BindOnce,callBack,second,base,first
来源: https://www.cnblogs.com/Galesaur-wcy/p/16387963.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有