分类: WINDOWS
2010-09-16 17:29:25
学习这个之前的最大动力来源于,对如下函数的思考..
ref:
boost::function
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();
}
return 0;
}
要知道如果你直接去阅读boost的源码.那么估计8成会被一大堆宏搞得不知道如何阅读代码.
就比如function这个类的某个特化版本,如下:
template
class function
: public BOOST_FUNCTION_FUNCTION
{
typedef BOOST_FUNCTION_FUNCTION
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
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
int>::type = 0
) :
base_type(f)
{
}
template
function(Functor f, Allocator a
,typename enable_if_c<
(boost::type_traits::ice_not<
(is_integral
int>::type = 0
) :
base_type(f,a)
{
}
function(clear_type*) : base_type() {}
function(const self_type& f) : base_type(static_cast
function(const base_type& f) : base_type(static_cast
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
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
int>::type = 0
)
你可以理解为当用户构造functor的时候输入了一个整形兼容的简单类型,比如0,0.1 .
再具体点如果输入0,那么实际上
typename boost::enable_if_c
就是上面这句话,而enable_if_c
所以重载决议会失败;那么失败后选择谁呢?
如果尝试这个的话,你会发现false之后的特化版本根本没有定义type这个类型,因此直接编译器就会报错!
boost::enable_if_c
我们顺便解决第一个问题, 即选择这个版本:
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::
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
int>::type = 0
) :
function_base()
{
this->
}
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
{
using detail::function::vtable_base;
typedef typename detail::function::get_function_tag
typedef detail::function::get_invoker0
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
这句话实际上提取了输入的functor属于哪一类
template
class get_function_tag
{
typedef typename mpl::if_c<(is_pointer
function_ptr_tag,
function_obj_tag>::type ptr_or_obj_tag;
typedef typename mpl::if_c<(is_member_pointer
member_ptr_tag,
ptr_or_obj_tag>::type ptr_or_obj_or_mem_tag;
typedef typename mpl::if_c<(is_reference_wrapper
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
typedef typename get_invoker::
template apply
handler_type;
上述两句,实际上使用了如下apply...
template<>
struct get_invoker0
{
template
struct apply
{
};
..........
template
struct get_function_invoker0
{
typedef typename mpl::if_c<(is_void
function_invoker0< FunctionPtr, R >
>::type type;
};
template
struct void_function_invoker0
{
static void
{
FunctionPtr f = reinterpret_cast
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
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
function0
{
if (this->empty())
boost::throw_exception(bad_function_call());
return reinterpret_cast
(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
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
// 下面invoke将拿到 void_invoker2
typedef detail::function::get_invoker2
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
if (boost::has_trivial_copy_constructor
boost::has_trivial_destructor
detail::function::function_allows_small_object_optimization
value |= (std::size_t)0x01; // 这个有关smallobject存储不属本文重点
vtable = reinterpret_cast
} 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
// f实际上是一个mem_func 的仿函数 ,这次呼叫在内部被转换为 (a0->*ptr)(a1);
f( a0 , a1);
}
};