`
qqsunkist
  • 浏览: 33200 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

探究boost::bind的实现

    博客分类:
  • C++
阅读更多

泛化的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::asio::serialport实现串口通信

    boost::bind(handle_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); ``` 5. **关闭串口**: 完成通信后,别忘了关闭串口: ```cpp serial.close(); ``` 在...

    c++ boost bind

    其中,`boost::bind`是Boost库中的一个关键组件,用于实现函数对象的绑定,它类似于C++11中的std::bind,但在C++98/03标准下非常有用。 `boost::bind`的主要功能是将函数或成员函数与部分参数预先绑定在一起,形成...

    tomcat 启动:Address already in use: JVM_Bind <null>:8080

    然而,在启动 Tomcat 时,偶尔会遇到报错“Address already in use: JVM_Bind &lt;null&gt;:8080”,这意味着端口 8080 已经被占用。 为什么会出现这种情况?这通常是因为某个进程已经占用了端口 8080,阻止了 Tomcat 的...

    通过c++11的std::bind及std::function实现类方法回调,模拟Qt实现信号槽

    c++11引入了std::bind及std::function,实现了函数的存储和绑定,即先将可调用的对象保存起来,在需要的时候再调用。定义了SignalObject信号类和SlotObject槽类,其中信号类中的 std::function(int)&gt; _call就是要...

    基于boost的bind与function的消息处理框架

    在IT领域,Boost库是一个非常重要的C++工具集,它为C++标准库提供了许多扩展功能,其中`bind`和`function`是两个非常实用的工具。本文将深入探讨这两个概念以及如何在消息处理框架中使用它们。 首先,让我们了解`...

    java.net.BindException: Address already in use: JVM_Bind :8088(端口冲突)

    在myeclipse中将html文件改成jsp文件时myeclipse卡住;将之前的任务关掉;再打开时多次部署项目的时候报错

    一个基于Boost::Process & ZeroMQ方案的C++多进程并发muiltprocessing-master.zip

    Boost库和ZeroMQ(又名0MQ、ZMQ或ZeroMQ)是两个强大的工具,可以用于实现这样的功能。Boost::Process库提供了在C++中方便地创建和管理进程的接口,而ZeroMQ则是一个轻量级的消息中间件,它提供了高效的进程间通信...

    深入实践Boost:Boost程序库开发的94个秘笈(中文版)

    4. **函数对象和绑定器**:Boost.Function和Boost.Bind使得函数对象和成员函数能够更加灵活地组合和使用,提高了代码的可读性和可维护性。 5. **并发编程**:Boost.Thread库提供了线程、互斥量、条件变量等工具,...

    boost asio tcp服务器的实现

    首先,我们需要创建一个`boost::asio::ip::tcp::endpoint`对象,指定服务器的IP地址和端口号,然后调用acceptor的`open`方法打开对应的协议族,`bind`方法绑定到指定端口。 2. **监听客户端连接** 调用`acceptor`...

    Bug:Address already in use: JVM_Bind问题之总结

    在编程和系统开发过程中,我们可能会遇到一个常见的错误——"Address already in use: JVM_Bind"。这个错误通常发生在尝试绑定一个已经被占用的网络端口时。本文将深入探讨这个问题,提供解决方案,并从源码角度解析...

    解决jvmjava.net.BindException: 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 和 std::bind,它们都是基于 C++ 11 的新特性,用于实现函数指针的功能。下面将详细介绍这两个功能的使用和区别。 std::function 是一个可调用对象的包装器,用于...

    Beyond The C++ Standard Library - An Introduction To Boost (2005)

    5. **函数对象和绑定**:介绍`boost::function`和`boost::bind`,这些工具可以简化函数对象的使用,实现函数的封装和部分应用。 6. **并发与多线程**:涵盖`boost::thread`库,包括线程创建、同步原语(如互斥量、...

    boost最新官方帮助文档1.56.0

    5. **模式与设计**:Boost提供了多种设计模式实现,如工厂模式(`boost::factory`)、观察者模式(`boost::signals2`)和策略模式(`boost::policy`)。此外,`boost::variant`和`boost::any`可以处理多种类型的数据...

    boost API 中文库

    5. **函数对象和绑定器**:boost::function和boost::bind允许将函数、成员函数和函数对象组合在一起,创建新的可调用对象。 6. **正则表达式**:boost::regex库提供了强大的正则表达式匹配和搜索功能,比C++标准库...

    Boost程序库完全开发指南

    - **图形界面**:Boost库虽然不直接提供GUI工具,但其`boost::signal`等组件可与其他库结合实现事件驱动编程。 5. **Boost与C++标准库的关系**:Boost库经常被C++标准委员会作为参考,许多Boost组件最终被纳入C++...

    boost完美组合(你不用再找其他的boost书籍了)

    `boost::bind`和`boost::function`提供了函数对象和函数指针的通用接口;`boost::spirit`则是一个强大的解析器库,用于构建复杂语法解析器。 3. **boost-thread.pdf**: Boost.Thread库是C++线程编程的重要资源,...

    Boost视频教程

    - **知识点**:Boost.Lambda和Boost.Bind为C++提供了强大的函数式编程能力。 - **应用技巧**: - 利用`boost::lambda`快速定义简单的匿名函数,简化代码。 - 通过`boost::bind`绑定函数参数,创建新的可调用对象。...

    实现了类似std::function /bind /shared_ptr功能

    实现了标准库中的function和bind以及智能指针,linux/windows通用。由于公司GCC版本较老,嵌入式的gcc也无法由我来控制,windows上使用的一些类似智能指针,在linux上无法使用,甚是不爽,于是自己手动写了一份,...

    C++ STL 内 std::{bind/tuple/function} 简单实现

    实现`bind`需要用到`tuple`来保存参数,以及占位符`_1`、`_2`等来表示参数的位置。`bind`的关键在于解析占位符并正确地将参数传递给目标函数。这通常通过特化模板和递归实现。例如: ```cpp class _unwrap_tuple { ...

Global site tag (gtag.js) - Google Analytics