chrome-base 如何实现一个BindOnce

ops/2025/2/11 6:59:52/

考虑一个问题:

    worker_thread.task_runner()->PostDelayedTask(FROM_HERE, base::BindOnce(&Ref::Foo, ref, 1), base::Milliseconds(1000));

BindOnce 是如何实现的呢?

翻看源码:base\functional\bind.h

写的 非常简洁

// Bind as OnceCallback.
template <typename Functor, typename... Args>
inline OnceCallback<internal::MakeUnboundRunType<Functor, Args...>> BindOnce(Functor&& functor,Args&&... args) {static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||(std::is_rvalue_reference<Functor&&>() &&!std::is_const<std::remove_reference_t<Functor>>()),"BindOnce requires non-const rvalue for OnceCallback binding."" I.e.: base::BindOnce(std::move(callback)).");static_assert(std::conjunction<internal::AssertBindArgIsNotBasePassed<std::decay_t<Args>>...>::value,"Use std::move() instead of base::Passed() with base::BindOnce()");return internal::BindImpl<OnceCallback>(std::forward<Functor>(functor),std::forward<Args>(args)...);
}

重要的也就是最后一句:构造一个BindImpl,同时完美转发

internal::BindImpl<OnceCallback>(std::forward<Functor>(functor),std::forward<Args>(args)...)

base\functional\bind_internal.h

template <template <typename> class CallbackT,typename Functor,typename... Args>
decltype(auto) BindImpl(Functor&& functor, Args&&... args) {// This block checks if each |args| matches to the corresponding params of the// target function. This check does not affect the behavior of Bind, but its// error message should be more readable.static constexpr bool kIsOnce = IsOnceCallback<CallbackT<void()>>::value;using Helper = BindTypeHelper<Functor, Args...>;using FunctorTraits = typename Helper::FunctorTraits;using BoundArgsList = typename Helper::BoundArgsList;using UnwrappedArgsList =MakeUnwrappedTypeList<kIsOnce, FunctorTraits::is_method, Args&&...>;using BoundParamsList = typename Helper::BoundParamsList;static_assert(MakeFunctorTraits<Functor>::is_stateless,"Capturing lambdas and stateful lambdas are intentionally not supported. ""Please use base::Bind{Once,Repeating} directly to bind arguments.");static_assert(AssertBindArgsValidity<std::make_index_sequence<Helper::num_bounds>,BoundArgsList, UnwrappedArgsList,BoundParamsList>::ok,"The bound args need to be convertible to the target params.");using BindState = MakeBindStateType<Functor, Args...>;using UnboundRunType = MakeUnboundRunType<Functor, Args...>;using Invoker = Invoker<BindState, UnboundRunType>;using CallbackType = CallbackT<UnboundRunType>;// Store the invoke func into PolymorphicInvoke before casting it to// InvokeFuncStorage, so that we can ensure its type matches to// PolymorphicInvoke, to which CallbackType will cast back.using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;PolymorphicInvoke invoke_func;if constexpr (kIsOnce) {invoke_func = Invoker::RunOnce;} else {invoke_func = Invoker::Run;}using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;return CallbackType(BindState::Create(reinterpret_cast<InvokeFuncStorage>(invoke_func),std::forward<Functor>(functor), std::forward<Args>(args)...));
}

BindState的实现

// This stores all the state passed into Bind().
template <typename Functor, typename... BoundArgs>
struct BindState final : BindStateBase {using IsCancellable = std::bool_constant<CallbackCancellationTraits<Functor,std::tuple<BoundArgs...>>::is_cancellable>;template <typename ForwardFunctor, typename... ForwardBoundArgs>static BindState* Create(BindStateBase::InvokeFuncStorage invoke_func,ForwardFunctor&& functor,ForwardBoundArgs&&... bound_args) {// Ban ref counted receivers that were not yet fully constructed to avoid// a common pattern of racy situation.BanUnconstructedRefCountedReceiver<ForwardFunctor>(bound_args...);// IsCancellable is std::false_type if// CallbackCancellationTraits<>::IsCancelled returns always false.// Otherwise, it's std::true_type.return new BindState(IsCancellable{}, invoke_func,std::forward<ForwardFunctor>(functor),std::forward<ForwardBoundArgs>(bound_args)...);}Functor functor_;std::tuple<BoundArgs...> bound_args_;private:static constexpr bool is_nested_callback =MakeFunctorTraits<Functor>::is_callback;template <typename ForwardFunctor, typename... ForwardBoundArgs>explicit BindState(std::true_type,BindStateBase::InvokeFuncStorage invoke_func,ForwardFunctor&& functor,ForwardBoundArgs&&... bound_args): BindStateBase(invoke_func,&Destroy,&QueryCancellationTraits<BindState>),functor_(std::forward<ForwardFunctor>(functor)),bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {// We check the validity of nested callbacks (e.g., Bind(callback, ...)) in// release builds to avoid null pointers from ending up in posted tasks,// causing hard-to-diagnose crashes. Ideally we'd do this for all functors// here, but that would have a large binary size impact.if (is_nested_callback) {CHECK(!IsNull(functor_));} else {DCHECK(!IsNull(functor_));}}template <typename ForwardFunctor, typename... ForwardBoundArgs>explicit BindState(std::false_type,BindStateBase::InvokeFuncStorage invoke_func,ForwardFunctor&& functor,ForwardBoundArgs&&... bound_args): BindStateBase(invoke_func, &Destroy),functor_(std::forward<ForwardFunctor>(functor)),bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {// See above for CHECK/DCHECK rationale.if (is_nested_callback) {CHECK(!IsNull(functor_));} else {DCHECK(!IsNull(functor_));}}~BindState() = default;static void Destroy(const BindStateBase* self) {delete static_cast<const BindState*>(self);}
};

其中有两个成员变量:

  Functor functor_;std::tuple<BoundArgs...> bound_args_;

其中用到了std::tuple ??

为什么chromium不使用std::bind

相对于std::bind, base::Bind能够帮助我们减少生命周期缺陷:

在使用 base::BindOnce() 方法产生一个 base::OnceClosure 的时候,一般会传递一个 base::WeakPrt,而不是一个裸指针。base::WeakPrt能确保所指向的对象销毁时,绑定在对象上的回调能被取消。否则一般会产生一个段错误。

如果声明裸指针则必须使用Unretained符号。

chromium bind出现的更早,且通过BindOnce与BindRepeating 对生命周期做了更加细致的区分,C++ 14之后好像也有了类似的bind?

c++中的元组

元组基本使用

https://en.cppreference.com/w/cpp/utility/tuple/get c11中引入了 std::tuple,并在后续的c14中扩充了使用方式

#include <iostream>
#include <string>
#include <tuple>int main()
{auto t = std::make_tuple(1, "Foo", 3.14);// index-based accessstd::cout << "(" << std::get<0>(t) << ", " << std::get<1>(t)<< ", " << std::get<2>(t) << ")\n";// type-based access (C++14 or later)std::cout << "(" << std::get<int>(t) << ", " << std::get<const char*>(t)<< ", " << std::get<double>(t) << ")\n";// Note: std::tie and structured binding may also be used to decompose a tuple
}

元组用作函数参数

#include <iostream>
#include <string>
#include <tuple>void foo(int a1, const char* a2, double a3) {// Do somethingstd::cout << a1 << std::endl;std::cout << a2 << std::endl;std::cout << a3 << std::endl;
}int main() {auto t = std::make_tuple(1, "Foo", 3.14);foo(std::get<0>(t), std::get<1>(t), std::get<2>(t));return 0;
}

观察后会发现,配合c++11的可变参数模板,可以写出这样一个函数,整体如下:

https://zhuanlan.zhihu.com/p/465077081

#include <iostream>
#include <string>
#include <tuple>void foo(int a1, const char* a2, double a3) {// Do somethingstd::cout << a1 << std::endl;std::cout << a2 << std::endl;std::cout << a3 << std::endl;
}template <typename F, typename T, std::size_t... I>
void bar(F f, const T& t) {f(std::get<I>(t)...);
}// 或者这样定义也可以
template <std::size_t... I, typename F, typename T>
void bar2(F f, const T& t) {f(std::get<I>(t)...);
}int main() {auto t = std::make_tuple(1, "Foo", 3.14);//foo(std::get<0>(t), std::get<1>(t), std::get<2>(t));bar<decltype(foo), decltype(t), 0, 1, 2>(foo, t);// 少了函数类型和调用类型的模板,这里编译器帮我们做了类型推导bar2<0, 1, 2>(foo, t);return 0;
}

使用std::index_sequence

上述调用方式比较笨拙,不够优雅灵活,还需要手动写模板参数,其实看看0,1,2的序列,其实是从0到N-1的整数序列,c++14中正好提供了生成这种序列的机制:std::index_sequence,这里面有一系列的序列别名,我们这里更简便的使用index_sequence就能解决问题了,具体如下

https://zhuanlan.zhihu.com/p/490967621

  /// Alias template index_sequencetemplate<size_t... _Idx>using index_sequence = integer_sequence<size_t, _Idx...>;/// Alias template make_index_sequencetemplate<size_t _Num>using make_index_sequence = make_integer_sequence<size_t, _Num>;/// Alias template index_sequence_fortemplate<typename... _Types>using index_sequence_for = make_index_sequence<sizeof...(_Types)>;

有了这个基础设施以后我们就可以这样写代码了:

https://blog.csdn.net/qq_51986723/article/details/127602490 std::index_sequence如何使用

#include <iostream>
#include <string>
#include <tuple>void foo(int a1, const char* a2, double a3) {// Do somethingstd::cout << a1 << std::endl;std::cout << a2 << std::endl;std::cout << a3 << std::endl;
}template <typename F, typename T, std::size_t... I>
void barImpl(F f, const T& t, std::index_sequence<I...>) {f(std::get<I>(t)...);
}template<typename F, typename T>
void bar(F f, const T& t) {barImpl(f, t, std::make_index_sequence<std::tuple_size<T>::value>());
}int main() {auto t = std::make_tuple(1, "Foo", 3.14);bar<decltype(foo), decltype(t)>(foo, t);return 0;
}

增加返回值

上面的虽然写起来比较简单了,但是没有返回值,我们可以采用decltype特性来解决。

#include <iostream>
#include <string>
#include <tuple>void foo(int a1, const char* a2, double a3) {// Do somethingstd::cout << a1 << std::endl;std::cout << a2 << std::endl;std::cout << a3 << std::endl;
}template <typename F, typename T, std::size_t... I>
auto barImpl(F f, const T& t, std::index_sequence<I...>) -> decltype(f(std::get<I>(t)...)){return f(std::get<I>(t)...);
}template<typename F, typename T>
auto bar(F f, const T& t) -> decltype(barImpl(f, t, std::make_index_sequence<std::tuple_size<T>::value>())) {return barImpl(f, t, std::make_index_sequence<std::tuple_size<T>::value>());
}
int main() {auto t = std::make_tuple(1, "Foo", 3.14);bar<decltype(foo), decltype(t)>(foo, t);return 0;

这样就使用任意的函数调用了,包括带返回值的和不带返回值的。

最终版本

  1. 封装一个万能调用类 +

  2. 直接传参

  3. 完美转发

将调用函数及参数封装进类中,在需要的时候调用Run一下就好了。

#include <iostream>
#include <string>
#include <tuple>template <typename Functor, typename... BoundArgs>
class OnceCallBack {
private:template <typename Functor1, typename TupleType1, std::size_t... I1>static auto RunImpl(Functor1 f, const TupleType1& t, std::index_sequence<I1...>) -> decltype(f(std::get<I1>(t)...)) {return f(std::get<I1>(t)...);}Functor* f_;std::tuple<BoundArgs...> t_;
public:OnceCallBack(Functor&& f, BoundArgs&&... t): f_(std::forward<Functor>(f)), t_(std::forward<BoundArgs>(t)...) {}auto Run() -> decltype(RunImpl(f_, t_, std::make_index_sequence<std::tuple_size<std::tuple<BoundArgs...>>::value>())) {return RunImpl(f_, t_, std::make_index_sequence<std::tuple_size<std::tuple<BoundArgs...>>::value>());}
};double foo_ret(double a1, char a2, const char* a3) {// Do somethingstd::cout << a1 << std::endl;std::cout << a2 << std::endl;std::cout << a3 << std::endl;return 1.2;
}int main() {// 函数模板参数类型编译器可以推导,但是类不行,所以这里需要很繁琐的传递参数类型OnceCallBack<decltype(foo_ret), decltype(1.0), decltype('c'), decltype("this is my test")> callback(std::move(foo_ret), 1.0, 'c', "this is my test");std::cout << callback.Run() << std::endl;
}

到这里,是不是已经很熟悉了。至此,一个最简单的base::Bind 方法就算实现完成了。

Chromium中的base::Bind

chromium中除了OnceCallBack还有RepeatingCallback,另外考虑的各种对象的生命周期问题,这样就涉及到指针传递,所以做的更为复杂,主要涉及的类包括:

  • BindStateBase

  • BindState

  • OnceCallBack

  • RepeatingCallback

  • Invoker

  • UnWrap

看到的也就是开头看到的源码

std::bind

C++11 bind绑定器,是一个函数模板 ,可以自动推演模板类型参数=> 返回的结果还是一个函数对象

bind占位符最多有20个参数

#include <iostream>
#include <typeinfo>
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include <thread>
using namespace std;
using namespace placeholders;/*
C++11 bind绑定器 => 返回的结果还是一个函数对象
*/void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }
class Test
{
public:int sum(int a, int b) { return a + b; }
};
int main()
{//bind是函数模板 可以自动推演模板类型参数bind(hello, "hello bind!")();//返回的结果是绑定器,也就是函数对象 最后一个()表示调用函数对象的operator() cout << bind(sum, 10, 20)() << endl;cout << bind(&Test::sum, Test(), 20, 30)() << endl;//参数占位符  绑定器出了语句,无法继续使用//只是占位的作用,调用的时候就要传递参数了 //书写的时候使用多少个占位符,就是意味着用户调用的时候要传入几个参数bind(hello, placeholders::_1)("hello bind 2!");cout << bind(sum, placeholders::_1, placeholders::_2)(200, 300) << endl;//此处把bind返回的绑定器binder就复用起来了function<void(string)> func1 = bind(hello, _1);func1("hello china!");func1("hello shan xi!");func1("hello si chuan!");return 0;
}
  /***  @brief Function template for std::bind.*  @ingroup binders*/template<typename _Func, typename... _BoundArgs>inline typename_Bind_helper<__is_socketlike<_Func>::value, _Func, _BoundArgs...>::typebind(_Func&& __f, _BoundArgs&&... __args){typedef _Bind_helper<false, _Func, _BoundArgs...> __helper_type;return typename __helper_type::type(std::forward<_Func>(__f),std::forward<_BoundArgs>(__args)...);}
---------------------------------------------------------------------------------------------------------------template<bool _SocketLike, typename _Func, typename... _BoundArgs>struct _Bind_helper: _Bind_check_arity<typename decay<_Func>::type, _BoundArgs...>{typedef typename decay<_Func>::type __func_type;typedef _Bind<__func_type(typename decay<_BoundArgs>::type...)> type;};
--------------------------------------------------template<typename _Functor, typename... _Bound_args>class _Bind<_Functor(_Bound_args...)>: public _Weak_result_type<_Functor>{typedef typename _Build_index_tuple<sizeof...(_Bound_args)>::__type_Bound_indexes;_Functor _M_f;tuple<_Bound_args...> _M_bound_args;// Call unqualifiedtemplate<typename _Result, typename... _Args, std::size_t... _Indexes>_Result__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>){return std::__invoke(_M_f,_Mu<_Bound_args>()(std::get<_Indexes>(_M_bound_args), __args)...);}// Call as consttemplate<typename _Result, typename... _Args, std::size_t... _Indexes>_Result__call_c(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) const{return std::__invoke(_M_f,_Mu<_Bound_args>()(std::get<_Indexes>(_M_bound_args), __args)...);}// Call as volatiletemplate<typename _Result, typename... _Args, std::size_t... _Indexes>_Result__call_v(tuple<_Args...>&& __args,_Index_tuple<_Indexes...>) volatile{return std::__invoke(_M_f,_Mu<_Bound_args>()(__volget<_Indexes>(_M_bound_args), __args)...);}// Call as const volatiletemplate<typename _Result, typename... _Args, std::size_t... _Indexes>_Result__call_c_v(tuple<_Args...>&& __args,_Index_tuple<_Indexes...>) const volatile{return std::__invoke(_M_f,_Mu<_Bound_args>()(__volget<_Indexes>(_M_bound_args), __args)...);}template<typename _BoundArg, typename _CallArgs>using _Mu_type = decltype(_Mu<typename remove_cv<_BoundArg>::type>()(std::declval<_BoundArg&>(), std::declval<_CallArgs&>()) );template<typename _Fn, typename _CallArgs, typename... _BArgs>using _Res_type_impl= typename result_of< _Fn&(_Mu_type<_BArgs, _CallArgs>&&...) >::type;template<typename _CallArgs>using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;template<typename _CallArgs>using __dependent = typenameenable_if<bool(tuple_size<_CallArgs>::value+1), _Functor>::type;template<typename _CallArgs, template<class> class __cv_quals>using _Res_type_cv = _Res_type_impl<typename __cv_quals<__dependent<_CallArgs>>::type,_CallArgs,typename __cv_quals<_Bound_args>::type...>;public:template<typename... _Args>explicit _Bind(const _Functor& __f, _Args&&... __args): _M_f(__f), _M_bound_args(std::forward<_Args>(__args)...){ }template<typename... _Args>explicit _Bind(_Functor&& __f, _Args&&... __args): _M_f(std::move(__f)), _M_bound_args(std::forward<_Args>(__args)...){ }_Bind(const _Bind&) = default;_Bind(_Bind&& __b): _M_f(std::move(__b._M_f)), _M_bound_args(std::move(__b._M_bound_args)){ }// Call unqualifiedtemplate<typename... _Args,typename _Result = _Res_type<tuple<_Args...>>>_Resultoperator()(_Args&&... __args){return this->__call<_Result>(std::forward_as_tuple(std::forward<_Args>(__args)...),_Bound_indexes());}// Call as consttemplate<typename... _Args,typename _Result = _Res_type_cv<tuple<_Args...>, add_const>>_Resultoperator()(_Args&&... __args) const{return this->__call_c<_Result>(std::forward_as_tuple(std::forward<_Args>(__args)...),_Bound_indexes());}#if __cplusplus > 201402L
# define _GLIBCXX_DEPR_BIND \[[deprecated("std::bind does not support volatile in C++17")]]
#else
# define _GLIBCXX_DEPR_BIND
#endif// Call as volatiletemplate<typename... _Args,typename _Result = _Res_type_cv<tuple<_Args...>, add_volatile>>_GLIBCXX_DEPR_BIND_Resultoperator()(_Args&&... __args) volatile{return this->__call_v<_Result>(std::forward_as_tuple(std::forward<_Args>(__args)...),_Bound_indexes());}// Call as const volatiletemplate<typename... _Args,typename _Result = _Res_type_cv<tuple<_Args...>, add_cv>>_GLIBCXX_DEPR_BIND_Resultoperator()(_Args&&... __args) const volatile{return this->__call_c_v<_Result>(std::forward_as_tuple(std::forward<_Args>(__args)...),_Bound_indexes());}};

placeholders 占位符:最多支持20个


http://www.ppmy.cn/ops/157173.html

相关文章

第六届MathorCup高校数学建模挑战赛-A题:淡水养殖池塘水华发生及池水自净化研究

目录 摘要 1 问题的重述 2 问题的分析 2.1 问题一的分析 2.2 问题二的分析 2.3 问题三的分析 2.4 问题四的分析 2.5 问题五的分析 3. 问题的假设 4. 符号说明 5. 模型的建立与求解 5.1 问题一的建模与求解 5.1.1 分析对象与指标的选取 5.1.2 折线图分析 5.1.3 相关性分析 5.1.4…

Maven概述与安装

目录 Maven 概述 1. 什么是 Maven 2. Maven 的主要功能 3. Maven 的优势 Maven 安装 1. 系统要求 2. 下载 Maven 3. 解压 Maven 4. 配置环境变量 Windows 系统 1.配置环境变量&#xff1a; 2.验证安装&#xff1a; Linux 系统 1. 打开终端窗口 2. 打开 .bashrc 文…

STM32系统架构介绍

STM32系统架构 1. CM3/4系统架构2. CM3/4系统架构-----存储器组织结构2.1 寄存器地址映射&#xff08;特殊的存储器&#xff09;2.2 寄存器地址计算2.3 寄存器的封装 3. CM3/4系统架构-----时钟系统 STM32 和 ARM 以及 ARM7是什么关系? ARM 是一个做芯片标准的公司&#xff0c…

软考教材重点内容 信息安全工程师 第15章 网络安全主动防御技术与应用

目录 15.1.1 人侵阻断技术原理 15.1.2 人侵阻断技术应用 15.3 网络流量清洗技术与应用 15.3.1 网络流量清洗技术原理 15.3.2 网络流量清洗技术应用 15.4 可信计算技术与应用 15.4.1 可信计算技术原理 15.5 数字水印技术与应用 15.5.1 数字水印技术原理 15.5.2 数字水…

Spring Boot 和Tomcat的关系

Spring Boot 和 Tomcat 之间的关系可以从多个角度来详细阐述&#xff0c;包括它们的作用、工作原理以及如何协同工作。以下是详细的解析&#xff1a; 1. Spring Boot 的简介 Spring Boot 是一个基于 Spring 框架的开发工具&#xff0c;它的目标是简化 Spring 应用的开发。Spr…

机器学习基本概念(附代码)

这里的“机器”指的是计算机软硬件组织&#xff0c;而非传统的机械装置&#xff1b;而“学习”&#xff0c;则是指软件通过训练过程&#xff0c;其性能得以提升的过程。 一、算法与模型的关系 在机器学习领域&#xff0c;算法和模型是两个核心概念。算法是一种偏抽象的概念&a…

java练习(12)

ps&#xff1a;题目来自力扣 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一…

国产编辑器EverEdit - 查找功能详解

1 查找功能详解 1.1 应用场景 查找关键词应该是整个文本编辑/阅读活动中&#xff0c;操作频度非常高的一项&#xff0c;用好查找功能&#xff0c;不仅可以可以搜索到关键字&#xff0c;还可以帮助用户高效完成一些特定操作。 1.2 基础功能 1.2.1 基础查找功能 选择主菜单查…