Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145776
  • 博文数量: 124
  • 博客积分: 70
  • 博客等级: 民兵
  • 技术积分: 1745
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-24 13:49
文章分类

全部博文(124)

文章存档

2011年(55)

2010年(14)

2009年(30)

2008年(25)

我的朋友

分类: WINDOWS

2010-09-16 17:29:25

学习这个之前的最大动力来源于,对如下函数的思考..

ref:

 

boost::function task = boost::bind(boost::mem_fn(&CPromotionAppMgr::CheckRaredUsedTipsShell),
  this, ::GetCurrentThreadId() , lSlotID , m_lAtuhOKCnt, strUID);

 

因为这个用法后面隐藏了复杂的模板类型推导,但是让使用者的便利性大大提高;

在我的另外一篇学习blog http://blog.chinaunix.net/u/18544/showart.php?id=2321793

中我也分析了如何从loki中学习 functor,binder...

 

但是在bind1st建立完成后就碰到了这样的问题:

1. 如果我想将一个调用cache下来

2. 我不需要用户提供除了R之外的类型

 

那么同时两点都要满足的话, 至少还做不到,因此想到了研习boost::function...

 

先来做一个最简单的例子:

 

//#include "stdafx.h"

#include

class testfunctor0
{
public:
 void operator()()
 {
 }

 void test()
 {
 }
};


int _tmain(int argc, _TCHAR* argv[])
{
 {
  testfunctor0 t0;
  boost::function f0(t0);
  f0();
 }
 return 0;
}

 

要知道如果你直接去阅读boost的源码.那么估计8成会被一大堆宏搞得不知道如何阅读代码.

就比如function这个类的某个特化版本,如下:

template         BOOST_FUNCTION_TEMPLATE_PARMS>
class function
  : public BOOST_FUNCTION_FUNCTION
{
  typedef BOOST_FUNCTION_FUNCTION base_type;
  typedef function self_type;

 

这一堆宏如果你人工阅读,很快就发现层次很深,而且效率低.

我用的是vs2005,然后打开/P 选项,即开启宏扩展输出,得到了

经过模板实例化后被真正创建的类:

下面这个命令能够将大量的空行删除

perl -p -i.bak -e "s/^[\s\r\n]*$//g" functor.i

template
class function
  : public function0
{
  typedef function0 base_type;
  typedef function self_type;

  // 干啥的

  struct clear_type {};

public:

  function() : base_type() {}

  template
  function(Functor f

           ,typename enable_if_c<
                            (boost::type_traits::ice_not<
                          (is_integral::value)>::value),
                       int>::type = 0
           ) :
    base_type(f)
  {
  }
  template
  function(Functor f, Allocator a

           ,typename enable_if_c<
                            (boost::type_traits::ice_not<
                          (is_integral::value)>::value),
                       int>::type = 0
           ) :
    base_type(f,a)
  {
  }


  function(clear_type*) : base_type() {}
  function(const self_type& f) : base_type(static_cast(f)){}

  function(const base_type& f) : base_type(static_cast(f)){}

  self_type& operator=(const self_type& f)
  {
    self_type(f).swap(*this);
    return *this;
  }

  template

  typename enable_if_c<
                            (boost::type_traits::ice_not<
                         (is_integral::value)>::value),
                      self_type&>::type


  operator=(Functor f)
  {
    self_type(f).swap(*this);
    return *this;
  }

};

这个由模板生成的函数,有几点需要关注:

1. function0

2. cleartype?

3. enable_if_c ??

 

先从第三个问题说起:

(SFINAE)

%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/748139d003b74088a1ec9c07.html

大致的含义是,c++在执行重载决议的时候,即函数的参数决议的时候,如果发现,参数不匹配但是还是有其他重载可以匹配的就不产生失败.

 

因此:

function(Functor f, Allocator a

           ,typename enable_if_c<
                            (boost::type_traits::ice_not<
                          (is_integral::value)>::value),
                       int>::type = 0
           )

你可以理解为当用户构造functor的时候输入了一个整形兼容的简单类型,比如0,0.1 .

再具体点如果输入0,那么实际上

typename boost::enable_if_c::type i= 0;

就是上面这句话,enable_if_c 的话是没有定义类型type,

所以重载决议会失败;那么失败后选择谁呢?

如果尝试这个的话,你会发现false之后的特化版本根本没有定义type这个类型,因此直接编译器就会报错!

boost::enable_if_c::type i= 0;

我们顺便解决第一个问题, 即选择这个版本:

function(clear_type*) : base_type() {}

进行缺省构造.
 

分析到了这儿,发现function其实只是一层包装,它实际上将所有的实质性的活丢给了父类; 那么从中我们能发现什么?

 

1. 和我另外一篇blog中描述loki 的时候非常相似,functor本身的构造函数的模板参数直接丢给内层类来处理,手法非常的相似.

 

那么当时在loki时候为什么要这么做呢?

1. 当时是想得到一种类似于多态的能力,即通过一个handlerimpl 来表达不同的hanlder

 

那么这儿function 将整个用户输入的值,整体传给function0,本身并没有保留类似于impl的值;

因此只能说继续看function0 如何处理这个functor....

 

下面是部分精简后的function0的代码,已经对宏进行扩展....

 

template
  class function0 : public function_base
  {
  public:
    typedef R         result_type;
  private:
    typedef boost::detail::function::
basic_vtable0
< R  >     vtable_type;
    struct clear_type {};

  public:
    static const int args = 0;
 
    template
    struct sig
    {
      typedef result_type type;
    };

    static const int arity = 0; 

    typedef function0 self_type;

    function0() : function_base() { }
      
    template
    function0(Functor  f ,typename enable_if_c<
                            (boost::type_traits::ice_not<
                             (is_integral::value)>::value),
                                        int>::type = 0
                            ) :
      function_base()
    {
      this->
assign_to
(f);
    }
    
    function0(clear_type*) : function_base() { }

   

    ~function0() { clear(); }

    result_type operator()() const;
     
   private:
    struct dummy {
      void nonnull() {};
    };

    typedef void (dummy::*safe_bool)();

  public:
    operator safe_bool () const
      { return (this->empty())? 0 : &dummy::nonnull; }

    bool operator!() const
      { return this->empty(); }

  private:
   

    template
    void
assign_to
(Functor f)
    {
      using detail::function::vtable_base;

      typedef typename detail::function::get_function_tag::type tag;
      typedef detail::function::get_invoker0 get_invoker;
      typedef typename get_invoker::
                         template apply                        >
        handler_type;
     
      typedef typename handler_type::invoker_type invoker_type;
      typedef typename handler_type::manager_type manager_type;
     
      static vtable_type stored_vtable =
        { { &manager_type::manage }, &invoker_type::invoke };

      if (stored_vtable.assign_to(f, functor)) vtable = &stored_vtable.base;
      else vtable = 0;
    }

     };

  }

 

看红色部分,assign_to..

typedef typename detail::function::get_function_tag::type tag;

 

这句话实际上提取了输入的functor属于哪一类

 template
      class get_function_tag
      {
        typedef typename mpl::if_c<(is_pointer::value),
                                   function_ptr_tag,
                                   function_obj_tag>::type ptr_or_obj_tag;

        typedef typename mpl::if_c<(is_member_pointer::value),
                                   member_ptr_tag,
                                   ptr_or_obj_tag>::type ptr_or_obj_or_mem_tag;

        typedef typename mpl::if_c<(is_reference_wrapper::value),
                                   function_obj_ref_tag,
                                   ptr_or_obj_or_mem_tag>::type or_ref_tag;

      public:
        typedef or_ref_tag type;
      };

 即上面红色部分,能够推导出functor到底是function_ptr
  function_obj
      member_ptr
      function_obj_ref

 

像这个例子就推导为function_ptr....

typedef detail::function::get_invoker0 get_invoker;

typedef typename get_invoker::
                         template apply                        >
        handler_type;


上述两句,实际上使用了如下apply...

template<>
  struct get_invoker0
  {
   template
   struct apply
   {
   
typedef typename get_function_invoker0::type invoker_type;
    typedef functor_manager manager_type;
   };

 ..........

 template
   struct get_function_invoker0
   {
    typedef typename mpl::if_c<(is_void::value),
    
void_function_invoker0< FunctionPtr, R >,
     function_invoker0< FunctionPtr, R >
    >::type type;
   };

      template
      struct void_function_invoker0
      {
        static void
       
invoke(function_buffer& function_ptr )
        {
          FunctionPtr f = reinterpret_cast(
function
_ptr.func_ptr);
          f();
        }
      };

 

function_ptr 就是如下的定义,实际上用以存储各类ptr...

 

  /**
       * A buffer used to store small function objects in
       * boost::function. It is a union containing function pointers,
       * object pointers, and a structure that resembles a bound
       * member function pointer.
       */
      union function_buffer
      {
        // For pointers to function objects
        mutable void* obj_ptr;

        // For pointers to std::type_info objects
        struct type_t {
          // (get_functor_type_tag, check_functor_type_tag).
          const BOOST_FUNCTION_STD_NS::type_info* type;

          // Whether the type is const-qualified.
          bool const_qualified;
          // Whether the type is volatile-qualified.
          bool volatile_qualified;
        } type;

        // For function pointers of all kinds
        mutable void (*func_ptr)();

        // For bound member pointers
        struct bound_memfunc_ptr_t {
          void (X::*memfunc_ptr)(int);
          void* obj_ptr;
        } bound_memfunc_ptr;

        // For references to function objects. We explicitly keep
        // track of the cv-qualifiers on the object referenced.
        struct obj_ref_t {
          mutable void* obj_ptr;
          bool is_const_qualified;
          bool is_volatile_qualified;
        } obj_ref;

        // To relax aliasing constraints
        mutable char data;
      };

 

 

assign_to最后的几句代码,干了啥?

 

 static vtable_type stored_vtable =
        { { &manager_type::manage }, &invoker_type::invoke };

      if (stored_vtable.assign_to(f, functor)) vtable = &stored_vtable.base;
      else vtable = 0;

vtable_type  是啥???

 

 template
      struct basic_vtable0
      {
        typedef R         result_type;
        typedef result_type (*invoker_type)(function_buffer&);

        template
        bool assign_to(F f, function_buffer& functor)
        {
          typedef typename get_function_tag::type tag;
          return assign_to(f, functor, tag());
        }

      private:
       
        template
        bool
        assign_to(FunctionPtr f, function_buffer& functor, function_ptr_tag)
        {
          this->clear(functor);
          if (f) {
           
           
            functor.func_ptr = (void (*)())(f);
            return true;
          } else {
            return false;
          }
        }

...
  public:
       
vtable_base base;
        invoker_type invoker;
};

 

看到了,刚才那个代码最后实际上初始化了,上面红色两个字段,由于本身是结构体,所以如此初始化...

 

 if (stored_vtable.assign_to(f, functor)) vtable = &stored_vtable.base;
      else vtable = 0;

assign_to中第一个参数是用户输入functor, 第二个是function0 parent function_base的两个成员之一:

public:
  detail::function::vtable_base* vtable;
  mutable detail::function::function_buffer functor;

 

 

通过对function0::assign_to trace,最后发现 basic_vtable0::assign_to的调用,

     basic_vtable0在对function_ptr的时候不需要做什么存储特别操作,致使将union中的ptr赋值..

 

vtable = &stored_vtable.base;

这句话有点怪, 因为本身是结构,所以将结构的第一个成员赋值给vtable(basic_vtable0)之后,实际上地址并没有发生变化,致使理解变了....

 

我们继续....

来看看真正的调用.....

 

template
  typename function0::result_type
   function0::operator()() const
  {
    if (this->empty())
      boost::throw_exception(bad_function_call());

    return reinterpret_cast(vtable)->invoker
             (this->functor  );

 

 

由于vtable的指针确实指向了了basic_vtable0,因此拿出invoker就是

void_function_invoker0::invoker.........

 

 

1. 这个类的模板参数依然只是记录R,而将实际的functor 还是留给了构造函数...

2. functor 最终通过assign 进行了存储

3. function0 使用了一种特殊的存储结构vtable? ,保存functor,operator()()呼叫

 

这个手法,和我自己另外一个blog中的functorhandler 做法不同.

这个function0 还是外层对象,即本身依然不保存functor...

 

那么functor的保存,和再次使用,恰恰也是本文学习的重点.....

 

到这儿,大致可以知道:

1.  boost::function 通过对函数参数个数的推导,实际上将自己派生自不同的functionN, 由于大量的重复写functionN 导致代码过多,因此boost 使用了一些宏来表示

2.  functionN被确定后?(这个如何确定,下面继续研究下 ), 那么functionN内部直接通过basic_vtable 作为用户输入functor的存储。这儿我们对比下loki的做法,loki 在类似的地方采用了存储一个继承体系的基类的作为比如 auto_ptr > spImpl; 这个做法来通过多态达到目的; boost通过预测实际呼叫参数的数量,直接定义basic_vtableN来保存functor的最终呼叫形式。

3. basic_vtable 搞了一个union ,这个union可以接受任意的函数指针

4. functionN::operator()(xxx) 最终将调用转发给内部的basic_vtableN所存储的invoker

5. invoker 真正来自于对于用户输入functor 执行了4类型判断后的 void_function_invokerN /function_invokerN 定义的静态函数invoke

 

 

到了这儿,我们知道,如果我们愿意去写多个比如是两个吧,function0,function1,function2 , 同时也准备对应的vtable 或者说是functor holder 吧,那么基本上这些就可以基本模仿function<>.

 

读到这儿,虽然基本的function基本理解,不过还有两个问题:

1.  编译期boost是如何确定输入参数的个数,即boost复杂的一堆macro都基于知道输入functor的参数个数,从而确定到底是functionx的。

2.  (仿函数这个和基本funcptr其实是一致的,具体来说都是按值copy,更加复杂的ref我们这儿就不探究了)成员函数的处理

 

我们先来看第二个问题,其实还是老办法,写一个类似call,然后展开macro..

 

 

 

testfunctor0 t0;

boost::function<int (testfunctor0* , double)>   f(&testfunctor0::test2);

f(&t0);

 

经过boost的宏展开后,我们果然发现了function2..

 

由于已经有了上面关于function0的帖代码,因此我这儿只是找最关键的几个代码来帖。。。

 

   template

    void assign_to(Functor f /* it is a mem_fun_ptr*/)

    {

      using detail::function::vtable_base;

// 下面这句话将拿到memfunction tag,下面会有帖

      typedef typename detail::function::get_function_tag::type tag;

// 下面invoke将拿到 void_invoker2

      typedef detail::function::get_invoker2 get_invoker;

      typedef typename get_invoker::

                         template apply

                         T0 , T1>

        handler_type;

      typedef typename handler_type::invoker_type invoker_type;

      typedef typename handler_type::manager_type manager_type;

      static vtable_type stored_vtable =

        { { &manager_type::manage }, &invoker_type::invoke };

      if (stored_vtable.assign_to(f, functor)) {

        std::size_t value = reinterpret_cast(&stored_vtable.base);

        if (boost::has_trivial_copy_constructor::value &&

            boost::has_trivial_destructor::value &&

            detail::function::function_allows_small_object_optimization::value)

          value |= (std::size_t)0x01; // 这个有关smallobject存储不属本文重点

        vtable = reinterpret_cast(value);

      } else

        vtable = 0;

    }

 

 

template<

        typename FunctionPtr,

        typename R ,

         typename T0 , typename T1

        >

      struct void_function_invoker2 // 简单起见就贴invoker2的代码了

      {

        static void

        invoke(function_buffer& function_ptr ,

                T0 a0 , T1 a1)

        {

          //  拿到memfunc_ptr

          FunctionPtr f = reinterpret_cast(function_ptr.func_ptr);

          //   f实际上是一个mem_func 的仿函数 ,这次呼叫在内部被转换为 (a0->*ptr)(a1);

          f( a0 , a1);

        }

      };

 
 
 
其实分析到这儿,还有一个问题是这样的,或者说是一个基本性的问题,即为什么
boost::function 这样它就能够直接推导出到底是function1, 还是function2;
 
template         BOOST_FUNCTION_TEMPLATE_PARMS>
class function<BOOST_FUNCTION_PARTIAL_SPEC>
  : public BOOST_FUNCTION_FUNCTION
也就是上面红色部分文字的处理...
 
 
这儿有一篇文章:
 还是比较好的分析了这个问题。
 
那么看了之后我的理解是这样的:
 
1. boost 采用预处理的宏来推导这个问题.
2. 预处理宏的方式就是,反复处理某个文件实际上就是反复包含 functio_template.hpp
   而每次包含的时候,定义好那个数量值,比如=1,=2;包含结束之后将这个define undef掉,类似如下代码..
    #define NUM 2
    #include
    #undef NUM
 
    #define NUM 3
    #include
    #undef NUM
 
    .........
 
    实际上不用boost的预处理机制,也能够解决这个问题,就像boost本身也存在某种类似的处理。
 
    那么每次包含这个 function_template.hpp ....
    实际上产生了如下的特化类
 
    boost::function
    boost::function
 
    而这个特别背定义类的范畴内,由于上述的#define NUM ,那么它识别到的参数数量就是NUM;
 
    这样大概就解释了为什么每个boost::function  类似写法之后就可以知道有几个参数;
 
    这儿还有一个小问题,就是不是所有的c++编译器都能够识别类似于
 
    这种写法的特化处理; 如果是这样的话,就没那么便宜了,需要直接使用
    boost::function1, boost::function2 .....
 
    而这些带有数字的boost::function 是不支持这种带()的特殊写法的,只能标准的写...
 
 
   
   
阅读(874) | 评论(0) | 转发(0) |
0

上一篇:一步一步学习bind

下一篇:singleton 读后感

给主人留下些什么吧!~~