泛化的bind实用性很强,支持将泛化函数(函数、函数指针、仿函数、与成员函数)与运行期实参的动态绑定;在实际工程中也经常作为基础工具集之一被频繁地使用;我计划投入一定的精力,仔细阅读boost库中的基础工具集的代码实现(bind是第一个);旨在了解这些工具的内部实现机理同时也提高下自己的C++语言泛型编程技术,以备后续的实际工程中使用;
2005年,Andrei Alexandrescu就在他的C++巨著《C++设计新思维中》中的5.10节中提出了泛型化的函数绑定;bind的实现目标非常明确,就是在运行期实现针对泛化函数任意类型、任意个数的实参进行绑定;形式如下:
bind(f, _2, _1)(x, y); // f(y, x)
bind(g, _1, 9, _1)(x); // g(x, 9, x)
bind(g, _3, _3, _3)(x, y, z); // g(z, z, z)
bind(g, _1, _1, _1)(x, y, z); // g(x, x, x)
根据上述的形式,假如对泛化函数不考虑的话,至少涉及任意个参数(理论上),任意类型、任意位置的实参与函数绑定;另外,涉及占位符等,值拷贝、引用或者移动参数传递、返回值等细节技术问题。
我们一步一步先进行拆解;从上述调用的形式分析,bind函数是function template,其最重要的作用是(个人认为)是进行参数类型推导;function template会根据不同的参数个数,而被重载;实现代码中支持最多9个参数;代码如下:(此处列出的是两个参数的函数模板)
template<class R, class F, class A1, class A2> _bi::bind_t<R, F, typename _bi::list_av_2<A1, A2>::type> BOOST_BIND(F f, A1 a1, A2 a2) { typedef typename _bi::list_av_2<A1, A2>::type list_type; return _bi::bind_t<R, F, list_type> (f, list_type(a1, a2)); }1、 根据函数重载规则(9个实参范围内, A表示参数,英语Argument,A1, A2, …… A9分表表示9种实参类型,当然9个实参类型可重复,可相同)选择对应参数个数的bind函数模板会别Instantiation。此过程根据函数模板的特性,自然地推导出参数A1,A2;
2、被推导出的参数类型A1, A2,通过class template _bi::list_av_2 组成了一个由两个类型元素合成的类型列表;新组成的类型列表被typedef成list_type类型
3、list_type(a1, a2) 被显式地的调用构造函数;
4、bind的返回类型为 _bi::bind_t<R, F, list_type>; 且实际真正实现是通过调用class template _bi::bind_t的函数调用(operator () .... )的形式实现的。bind_t是被重载了的仿函数!
5、值得注意的是:F类型可以通过bind的调用,被推导出来;但是返回类型R, 目前还是推导不出来哦!
接下来,我们先看class template _bi::list_av_2的实现代码;
template<class T> class value { public: value(T const & t): t_(t) {} T & get() { return t_; } T const & get() const { return t_; } bool operator==(value const & rhs) const { return t_ == rhs.t_; } private: T t_; }; namespace _bi { // add_value // 参数类型是占位符时,add_value<T>::type是boost::arg<I>;与类型T无关了 template< class T, int I > struct add_value_2 { typedef boost::arg<I> type; }; //参数类型非占位符时,add_value<T>::type实际就是_bi::value<T> template< class T > struct add_value_2< T, 0 >{ typedef _bi::value< T > type; }; template<class T> struct add_value{ typedef typename add_value_2< T, boost::is_placeholder< T >::value >::type type; }; template<class T> struct add_value< value<T> >{ typedef _bi::value<T> type; }; template<class T> struct add_value< reference_wrapper<T> >{ typedef reference_wrapper<T> type; }; template<int I> struct add_value< arg<I> >{ typedef boost::arg<I> type; }; template<int I> struct add_value< arg<I> (*) () >{ typedef boost::arg<I> (*type) (); }; template<class R, class F, class L> struct add_value< bind_t<R, F, L> >{ typedef bind_t<R, F, L> type; }; } template<class A1, class A2> struct list_av_2 { typedef typename add_value<A1>::type B1; typedef typename add_value<A2>::type B2; typedef list2<B1, B2> type; };
这段代码中,主要就是理解add_value类模板针对各种情况的特化;单独看代码貌似比较难理解;以例子代入的话,会容易些;调用实例如下述代码:
#include <iostream> struct sum { typedef double result_type; double operator()(int a, double b) { return a+b; } }; int main(int argc, char *argv[]) { // 情况1 std::cout << boost::bind(sum(), _1, _2)(3, 4.0) << std::endl; // 情况2 std::cout << boost::bind(sum(), 3, 4.0)() << std::endl; // 情况3 std::cout << boost::bind(sum(), _1, 4.0)(3) << std::endl; return 0; }
情况1:A1: boost::arg<1>, A2: boost::arg<2>; B1: boost::arg<1>, B2: boost::arg<2>;
list_type = list2<B1, B2>
情况2:A1: int, A2: double; B1: value<int>, B2: value<double>;
list_type = list2<B1, B2>
情况3:A1: boost::arg<1>, A2: double; B1: boost::arg<1>, B2: value<double>;
list_type = list2<B1, B2>
根据argument deduction的结果进行分析,本次进行参数推导的参数列表是第一对圆括号内的。第二个实参列表并木有丢失!我们把第一对圆括号内的参数,简称第一参数列表;第二对圆括号内的参数,简称第二参数列表;注意,第二参数列表才是真正的实际参数;第一参数列表里可能包含占位符参数;
template< class A1, class A2 > class list2: private storage2< A1, A2 > { private: typedef storage2< A1, A2 > base_type; public: list2( A1 a1, A2 a2 ): base_type( a1, a2 ) {} A1 operator[] (boost::arg<1>) const { return base_type::a1_; } A2 operator[] (boost::arg<2>) const { return base_type::a2_; } A1 operator[] (boost::arg<1> (*) ()) const { return base_type::a1_; } A2 operator[] (boost::arg<2> (*) ()) const { return base_type::a2_; } template<class T> T & operator[] (_bi::value<T> & v) const { return v.get(); } template<class T> T const & operator[] (_bi::value<T> const & v) const { return v.get(); } template<class T> T & operator[] (reference_wrapper<T> const & v) const { return v.get(); } template<class R, class F, class L> typename result_traits<R, F>::type operator[] (bind_t<R, F, L> & b) const { return b.eval(*this); } template<class R, class F, class L> typename result_traits<R, F>::type operator[] (bind_t<R, F, L> const & b) const { return b.eval(*this); } template<class R, class F, class A> R operator()(type<R>, F & f, A & a, long){ return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); } template<class R, class F, class A> R operator()(type<R>, F const & f, A & a, long) const { return unwrapper<F const>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); } template<class F, class A> void operator()(type<void>, F & f, A & a, int){ unwrapper<F>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); } template<class F, class A> void operator()(type<void>, F const & f, A & a, int) const{ unwrapper<F const>::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]); } template<class A> bool operator()( type<bool>, logical_and & /*f*/, A & a, int ){ return a[ base_type::a1_ ] && a[ base_type::a2_ ]; } template<class A> bool operator()( type<bool>, logical_and const & /*f*/, A & a, int ) const{ return a[ base_type::a1_ ] && a[ base_type::a2_ ]; } template<class A> bool operator()( type<bool>, logical_or & /*f*/, A & a, int ){ return a[ base_type::a1_ ] || a[ base_type::a2_ ]; } template<class A> bool operator()( type<bool>, logical_or const & /*f*/, A & a, int ) const{ return a[ base_type::a1_ ] || a[ base_type::a2_ ]; } template<class V> void accept(V & v) const{ base_type::accept(v); } bool operator==(list2 const & rhs) const{ return ref_compare(base_type::a1_, rhs.a1_, 0) && ref_compare(base_type::a2_, rhs.a2_, 0); } };
list0, list1, list2, ... ... list9 是不同模板参数个数的listN 类模板实现;上述同样以list2为例说明list的实现;这个listN的类模板实现很重要,因为在接下来的真正被绑定的函数调用体被调用时_bi::bind_t<>::operator()(...),最终还是依赖于这个listN的实现;
listN因为负责最终存储传入的第二参数列表中的实参,就必然有随机读取任意参数的功能;除此之外,listN还负责最终的绑定函数体的调用;因此listN的实现,主要有三部分功能:
(1)存储最终调用函数体需要传入的参数;即,存储第二参数列表;
(2)operator[] 的运算符的重载;【类似随机迭代器,对保存的实参列表进行随机访问】
(3)operator() 运算符的重载; 【实现最终被绑定的泛化函数体的函数调用】
功能(1)是listN通过storageN的私有继承得以实现;此处的继承使用了私有继承,私有继承的实现语义是”用……来实现“(“is implemented in terms of ");且子类在其成员函数里调用基类公有成员时,使用base::member_语法; 按照上述调用代码逻辑情况1为例,传入参数a1,a2分别是 _1, _2,被存储在storageN里。
namespace boost { namespace _bi { // 1 template<class A1> struct storage1 { explicit storage1( A1 a1 ): a1_( a1 ) {} template<class V> void accept(V & v) const { BOOST_BIND_VISIT_EACH(v, a1_, 0); } A1 a1_; }; #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) && !defined( __BORLANDC__ ) template<int I> struct storage1< boost::arg<I> > { explicit storage1( boost::arg<I> ) {} template<class V> void accept(V &) const { } static boost::arg<I> a1_() { return boost::arg<I>(); } }; template<int I> struct storage1< boost::arg<I> (*) () > { explicit storage1( boost::arg<I> (*) () ) {} template<class V> void accept(V &) const { } static boost::arg<I> a1_() { return boost::arg<I>(); } }; #endif // 2 template<class A1, class A2> struct storage2: public storage1<A1> { typedef storage1<A1> inherited; storage2( A1 a1, A2 a2 ): storage1<A1>( a1 ), a2_( a2 ) {} template<class V> void accept(V & v) const { inherited::accept(v); BOOST_BIND_VISIT_EACH(v, a2_, 0); } A2 a2_; }; #if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) template<class A1, int I> struct storage2< A1, boost::arg<I> >: public storage1<A1> { typedef storage1<A1> inherited; storage2( A1 a1, boost::arg<I> ): storage1<A1>( a1 ) {} template<class V> void accept(V & v) const { inherited::accept(v); } static boost::arg<I> a2_() { return boost::arg<I>(); } }; template<class A1, int I> struct storage2< A1, boost::arg<I> (*) () >: public storage1<A1> { typedef storage1<A1> inherited; storage2( A1 a1, boost::arg<I> (*) () ): storage1<A1>( a1 ) {} template<class V> void accept(V & v) const { inherited::accept(v); } static boost::arg<I> a2_() { return boost::arg<I>(); } }; #endif
上述截取了部分storageN的类模板代码,可以看出storageN是个单继承体系,N表示了继承体系的总层级,例如storage2则表示整体继承体系一共2层,第一层存储了参数a1, 第二层公有继承于storage1,存储参数a2;至于为什么使用这样的实现方式进行存储,后续再深入研究;另外需要注意的是,是A1,A2的类型决定了参数存储方式;如果const reference&, 实参是以引用的形式存储的。后续值得关注下,A1,A2在经过函数模板的类型推导后,是否存有中间加工的元编程处理过程;
功能(3)的实现中使用了unwrapper类模板对F的外覆类进行解外覆处理;ref.hpp实际是boost中的另外一个基础核心工具集之一;后续单独研究下;
接下来,就是重点分析下,如何从占位符到实参的绑定,并且bind_t的对绑定函数的实际调用;这里自然要先粘贴下_bi::bind_t代码的实现摘要;
template< class A > struct list_add_cref { typedef A const & type; }; template< class A > struct list_add_cref< A& >{ typedef A & type; }; template<class R, class F, class L> class bind_t { private: F f_; L l_; public: typedef typename result_traits<R, F>::type result_type; typedef bind_t this_type; bind_t( F f, L const & l ): f_( f ), l_( l ) {} ... ... template<class A1, class A2> result_type operator()( A1 && a1, A2 && a2 ) { list2< typename list_add_cref<A1>::type, typename list_add_cref<A2>::type > a( a1, a2 ); return l_( type<result_type>(), f_, a, 0 ); } template<class A1, class A2> result_type operator()( A1 && a1, A2 && a2 ) const { list2< typename list_add_cref<A1>::type, typename list_add_cref<A2>::type > a( a1, a2 ); return l_( type<result_type>(), f_, a, 0 ); } …… …… template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2) { list2<A1 &, A2 &> a(a1, a2); BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0); } template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2) const { list2<A1 &, A2 &> a(a1, a2); BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0); } …… ……
上述第一参数列表推导后,加工处理后的组成的list_type类型,被推导成L类型;同时,根据第二参数列表的个数及类型,会找对应参数个数的operator()实例化的调用;第二参数列表会被推到成类型A1,A2 ....并组成另外一个listN类型且该类型的实例是a;如前所述,在组成listN的过程中,通过list_add_cref<T>元编程处理过程针对A1,A2推导的类型进行add const reference处理,以确保实参以最低成本存储在storageN中。L类型中的多个operator()的重载形式之一被调用时,list_type的operator[]实现了最终的占位符到实参(第二参数列表中的)的绑定;
相关推荐
boost::bind(handle_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); ``` 5. **关闭串口**: 完成通信后,别忘了关闭串口: ```cpp serial.close(); ``` 在...
其中,`boost::bind`是Boost库中的一个关键组件,用于实现函数对象的绑定,它类似于C++11中的std::bind,但在C++98/03标准下非常有用。 `boost::bind`的主要功能是将函数或成员函数与部分参数预先绑定在一起,形成...
然而,在启动 Tomcat 时,偶尔会遇到报错“Address already in use: JVM_Bind <null>:8080”,这意味着端口 8080 已经被占用。 为什么会出现这种情况?这通常是因为某个进程已经占用了端口 8080,阻止了 Tomcat 的...
c++11引入了std::bind及std::function,实现了函数的存储和绑定,即先将可调用的对象保存起来,在需要的时候再调用。定义了SignalObject信号类和SlotObject槽类,其中信号类中的 std::function(int)> _call就是要...
在IT领域,Boost库是一个非常重要的C++工具集,它为C++标准库提供了许多扩展功能,其中`bind`和`function`是两个非常实用的工具。本文将深入探讨这两个概念以及如何在消息处理框架中使用它们。 首先,让我们了解`...
在myeclipse中将html文件改成jsp文件时myeclipse卡住;将之前的任务关掉;再打开时多次部署项目的时候报错
Boost库和ZeroMQ(又名0MQ、ZMQ或ZeroMQ)是两个强大的工具,可以用于实现这样的功能。Boost::Process库提供了在C++中方便地创建和管理进程的接口,而ZeroMQ则是一个轻量级的消息中间件,它提供了高效的进程间通信...
4. **函数对象和绑定器**:Boost.Function和Boost.Bind使得函数对象和成员函数能够更加灵活地组合和使用,提高了代码的可读性和可维护性。 5. **并发编程**:Boost.Thread库提供了线程、互斥量、条件变量等工具,...
首先,我们需要创建一个`boost::asio::ip::tcp::endpoint`对象,指定服务器的IP地址和端口号,然后调用acceptor的`open`方法打开对应的协议族,`bind`方法绑定到指定端口。 2. **监听客户端连接** 调用`acceptor`...
在编程和系统开发过程中,我们可能会遇到一个常见的错误——"Address already in use: JVM_Bind"。这个错误通常发生在尝试绑定一个已经被占用的网络端口时。本文将深入探讨这个问题,提供解决方案,并从源码角度解析...
【Java中的`java.net.BindException: Address already in use: JVM_Bind`异常】 在Java编程中,当你尝试启动一个服务器端应用,如Tomcat,或者任何需要监听特定端口的服务时,可能会遇到`java.net.BindException: ...
C++ 11 中引入了两个重要的功能:std::function 和 std::bind,它们都是基于 C++ 11 的新特性,用于实现函数指针的功能。下面将详细介绍这两个功能的使用和区别。 std::function 是一个可调用对象的包装器,用于...
5. **函数对象和绑定**:介绍`boost::function`和`boost::bind`,这些工具可以简化函数对象的使用,实现函数的封装和部分应用。 6. **并发与多线程**:涵盖`boost::thread`库,包括线程创建、同步原语(如互斥量、...
5. **模式与设计**:Boost提供了多种设计模式实现,如工厂模式(`boost::factory`)、观察者模式(`boost::signals2`)和策略模式(`boost::policy`)。此外,`boost::variant`和`boost::any`可以处理多种类型的数据...
5. **函数对象和绑定器**:boost::function和boost::bind允许将函数、成员函数和函数对象组合在一起,创建新的可调用对象。 6. **正则表达式**:boost::regex库提供了强大的正则表达式匹配和搜索功能,比C++标准库...
- **图形界面**:Boost库虽然不直接提供GUI工具,但其`boost::signal`等组件可与其他库结合实现事件驱动编程。 5. **Boost与C++标准库的关系**:Boost库经常被C++标准委员会作为参考,许多Boost组件最终被纳入C++...
`boost::bind`和`boost::function`提供了函数对象和函数指针的通用接口;`boost::spirit`则是一个强大的解析器库,用于构建复杂语法解析器。 3. **boost-thread.pdf**: Boost.Thread库是C++线程编程的重要资源,...
- **知识点**:Boost.Lambda和Boost.Bind为C++提供了强大的函数式编程能力。 - **应用技巧**: - 利用`boost::lambda`快速定义简单的匿名函数,简化代码。 - 通过`boost::bind`绑定函数参数,创建新的可调用对象。...
实现了标准库中的function和bind以及智能指针,linux/windows通用。由于公司GCC版本较老,嵌入式的gcc也无法由我来控制,windows上使用的一些类似智能指针,在linux上无法使用,甚是不爽,于是自己手动写了一份,...
实现`bind`需要用到`tuple`来保存参数,以及占位符`_1`、`_2`等来表示参数的位置。`bind`的关键在于解析占位符并正确地将参数传递给目标函数。这通常通过特化模板和递归实现。例如: ```cpp class _unwrap_tuple { ...