我们要实现一个和function接口一样的类
首先function可以是void(int),void(int,int),void(int,int,int),最开始不明白如何实现,其实很简单,class特化而已。
template<typename Signature> class function;
template<typename R>
class function<R()>
{
//...
};
template<typename R,typename T1>
class function<R(T1)>
{
//...
};
template<typename R, typename T1,typename T2>
class function<R(T1,T2)>
{
//...
};
//...
这样根据不同的signature,就可以得到不同的function。
其次,我们的目标是能够支持函数指针和仿函数(成员函数可以通过仿函数来完成,下篇分析源码时介绍);
1.函数指针,函数指针只要能够保存函数地址,调用时使用指针来完成。
2.仿函数,这个只能利用拷贝构造new一个拷贝,将new出来的仿函数的指针保存下来,function析构时再delete。
要支持不同的类型,我第一时间想到的是接口,然后由子类实现,这也是我最开始对委托的理解,这样的做法效率比较低。从上面的分析可知,不管哪种类型,我们都需要保存指针,但是调用的时候,调用方法不同。而function<int(int)>,function<void(int,int)>类型的函数指针类型是不同的,但是存储到内存中都是一个指针,所以boot function定义了一个类型,
function_buffer,用来存储指针
union function_buffer
{
// For pointers to function objects
mutable void* obj_ptr;
// For function pointers of all kinds
mutable void(*func_ptr)();
};
所有的function都使用上述对象来存储。问题是如何调用,这可以通过函数指针类完成invoke,函数指针和仿函数调用不同的invoke指针来完成。
以function1为例,我们目前基本具备了以下代码:
#include <stdexcept>
template<typename Signature> class function;
/// \brief 当函数指针为空时抛出异常
class bad_function_call : public std::runtime_error
{
public:
bad_function_call() : std::runtime_error("call to empty boost::function") {}
};
template<typename R,typename T0>
class function<R(T0)>
{
public:
function()
{
invoker = nullptr;
}
typedef R(*invoker_type)(function_buffer&, T0);///<函数调用指针
R operator()(T0 a0) const
{
if (this->empty())
throw new bad_function_call();
return invoker(this->functor, a0);
}
bool empty() const
{
return !invoker;
}
private:
invoker_type invoker;
function_buffer functor;
};
有一点有问题,我们没有函数指针和仿函数对象的构造函数,同时,我们也没有对仿函数的指针delete。而且function需要拷贝构造,移动构造,这样我们使用一个新的函数指针,manager来操作,如下,通过传入不同的tag,我们就可以实现不同的操作。
enum functor_manager_operation_type {
clone_functor_tag,
move_functor_tag,
destroy_functor_tag
};
struct vtable_base
{
void(*manager)(const function_buffer& in_buffer,
function_buffer& out_buffer,
functor_manager_operation_type op);
};
我们的function变为
template<typename R,typename T0>
class function<R(T0)>
{
public:
function()
{
invoker = nullptr;
}
~function()
{
vtable->manager(f.functor, this->functor, destroy_functor_tag);
}
typedef R(*invoker_type)(function_buffer&, T0);
R operator()(T0 a0) const
{
if (this->empty())
throw new bad_function_call();
return invoker(this->functor, a0);
}
bool empty() const
{
return !invoker;
}
function(const function& f)
{
assign_to(f);
}
function(function&& f)
{
vtable->manager(f.functor, this->functor,move_functor_tag);
}
function& operator=(const function& f)
{
assign_to(f);
return *this;
}
function& operator=(function&& f)
{
if (&f == this)
return;
vtable->manager(f.functor, this->functor, move_functor_tag);
}
private:
vtable_base* vtable;
invoker_type invoker;
function_buffer functor;
void assign_to(const function& f)
{
vtable->manager(f.functor, this->functor, clone_functor_tag);
}
};
在加入构造函数之前,我们先实现manage的不同版本
template<typename Functor>
struct functor_manager_common
{
typedef Functor functor_type;
//函数指针
static inline void manage_ptr(const function_buffer& in_buffer, function_buffer& out_buffer,
functor_manager_operation_type op)
{
if (op == clone_functor_tag)
out_buffer.func_ptr = in_buffer.func_ptr;
else if (op == move_functor_tag) {
out_buffer.func_ptr = in_buffer.func_ptr;
in_buffer.func_ptr = 0;
}
else /* op == destroy_functor_tag */{
out_buffer.func_ptr = 0;
}
}
//仿函数指针
static inline void manager(const function_buffer& in_buffer, function_buffer& out_buffer,
functor_manager_operation_type op)
{
if (op == clone_functor_tag) {
const functor_type* f =static_cast<const functor_type*>(in_buffer.obj_ptr);
functor_type* new_f = new functor_type(*f);
out_buffer.obj_ptr = new_f;
}
else if (op == move_functor_tag) {
out_buffer.obj_ptr = in_buffer.obj_ptr;
in_buffer.obj_ptr = 0;
}
else /*op == destroy_functor_tag*/{
/* Cast from the void pointer to the functor pointer type */
functor_type* f =static_cast<functor_type*>(out_buffer.obj_ptr);
delete f;
out_buffer.obj_ptr = 0;
}
}
};
以上内容不难读懂,不多说了,再就是invoke的不同版本了,
template<typename FunctionPtr,typename R,typename T0>
struct function_invoker1
{
static R invoke(function_buffer& function_ptr,
T0 a0)
{
FunctionPtr f = reinterpret_cast<FunctionPtr>(function_ptr.func_ptr);
return f(a0);
}
};
template<typename FunctionObj,typename R,typename T0>
struct function_obj_invoker1
{
static R invoke(function_buffer& function_obj_ptr,T0 a0)
{
FunctionObj* f= reinterpret_cast<FunctionObj*>(function_obj_ptr.obj_ptr);
return (*f)(a0);
}
};
有了上面所有实现,我们就可以开始实现function的构造函数,我们希望根据不同的输入,去调用不同的invoke,manage,所以我们需要区分构造参数的不同,
这里需要使用元编程的一些轮子,如下
struct function_ptr_tag {};
struct function_obj_tag {};
template<typename F>
class get_function_tag
{
typedef typename boost::mpl::if_c<(boost::is_pointer<F>::value),
function_ptr_tag,
function_obj_tag>::type ptr_or_obj_tag;
public:
typedef ptr_or_obj_tag type;
};
这里if_c通过判断F是不是指针来决定function_tag的type类型是function_ptr_tag或者function_obj_tag,有了上面的努力,下面的代码就很简单了
template<typename Functor>
function(Functor const & f):
vtable(nullptr),
invoker(nullptr)
{
typedef typename get_function_tag<Functor>::type tag;
this->assign_to(f, tag());
}
这里的assign_to根据tag的不同,初始化不同参数,首先是函数指针
template<typename FunctionPtr>
void assign_to(FunctionPtr f, function_ptr_tag)
{
static vtable_base stored_vtable = { &functor_manager_common<FunctionPtr>::manage_ptr };
Clear();
if (f) {
functor.func_ptr = reinterpret_cast<void(*)()>(f);
}
vtable = &stored_vtable;
invoker = &function_invoker1<FunctionPtr, R, T0>::invoke;
}
接着仿函数
template<typename FunctionObj>
void assign_to(FunctionObj f, function_obj_tag)
{
static vtable_base stored_vtable = {&functor_manager_common<FunctionObj>::manager};
functor.obj_ptr = new FunctionObj(f);
vtable = &stored_vtable;
invoker=&function_obj_invoker1<FunctionObj, R, T0>::invoke;
}
至此,我们的一元function的代码编码完毕,完整代码如下,我加了一些测试代码:
#include <stdexcept>
#include <boost\mpl\if.hpp>
#include <boost\type_traits\is_pointer.hpp>
#include <assert.h>
union function_buffer
{
// For pointers to function objects
mutable void* obj_ptr;
// For function pointers of all kinds
mutable void(*func_ptr)();
};
template<typename Signature> class function;
class bad_function_call : public std::runtime_error
{
public:
bad_function_call() : std::runtime_error("call to empty boost::function") {}
};
enum functor_manager_operation_type {
clone_functor_tag,
move_functor_tag,
destroy_functor_tag
};
struct vtable_base
{
void(*manager)(const function_buffer& in_buffer,
function_buffer& out_buffer,
functor_manager_operation_type op);
};
template<typename Functor>
struct functor_manager_common
{
typedef Functor functor_type;
//函数指针
static inline void manage_ptr(const function_buffer& in_buffer, function_buffer& out_buffer,
functor_manager_operation_type op)
{
if (op == clone_functor_tag)
out_buffer.func_ptr = in_buffer.func_ptr;
else if (op == move_functor_tag) {
out_buffer.func_ptr = in_buffer.func_ptr;
in_buffer.func_ptr = 0;
}
else /* op == destroy_functor_tag */ {
out_buffer.func_ptr = 0;
}
}
//仿函数指针
static inline void manager(const function_buffer& in_buffer, function_buffer& out_buffer,
functor_manager_operation_type op)
{
if (op == clone_functor_tag) {
const functor_type* f = static_cast<const functor_type*>(in_buffer.obj_ptr);
functor_type* new_f = new functor_type(*f);
out_buffer.obj_ptr = new_f;
}
else if (op == move_functor_tag) {
out_buffer.obj_ptr = in_buffer.obj_ptr;
in_buffer.obj_ptr = 0;
}
else /*op == destroy_functor_tag*/ {
/* Cast from the void pointer to the functor pointer type */
functor_type* f = static_cast<functor_type*>(out_buffer.obj_ptr);
delete f;
out_buffer.obj_ptr = 0;
}
}
};
template<typename FunctionPtr,typename R,typename T0>
struct function_invoker1
{
static R invoke(function_buffer& function_ptr,T0 a0)
{
FunctionPtr f = reinterpret_cast<FunctionPtr>(function_ptr.func_ptr);
return f(a0);
}
};
template<typename FunctionObj,typename R,typename T0>
struct function_obj_invoker1
{
static R invoke(function_buffer& function_obj_ptr,T0 a0)
{
FunctionObj* f= reinterpret_cast<FunctionObj*>(function_obj_ptr.obj_ptr);
return (*f)(a0);
}
};
struct function_ptr_tag {};
struct function_obj_tag {};
template<typename F>
class get_function_tag
{
typedef typename boost::mpl::if_c<(boost::is_pointer<F>::value),
function_ptr_tag,
function_obj_tag>::type ptr_or_obj_tag;
public:
typedef ptr_or_obj_tag type;
};
template<typename R, typename T0>
class function<R(T0)>
{
typedef R(*invoker_type)(function_buffer&, T0);
public:
function()
{
invoker = nullptr;
}
template<typename Functor>
function(Functor const & f):
vtable(nullptr),
invoker(nullptr)
{
typedef typename get_function_tag<Functor>::type tag;
this->assign_to(f, tag());
}
~function()
{
Clear();
}
R operator()(T0 a0)
{
if (this->empty())
throw new bad_function_call();
return invoker(this->functor, a0);
}
bool empty() const
{
return !vtable;
}
function(const function& f)
{
assign_to(f);
}
function(function&& f)
{
vtable->manager(f.functor, this->functor, move_functor_tag);
}
function& operator=(const function& f)
{
assign_to(f);
return *this;
}
function& operator=(function&& f)
{
if (&f == this)
return;
vtable->manager(f.functor, this->functor, move_functor_tag);
}
private:
vtable_base* vtable;
invoker_type invoker;
function_buffer functor;
void Clear()
{
if (vtable) {
vtable->manager(functor, functor, destroy_functor_tag);
vtable = nullptr;
}
}
void assign_to(const function& f)
{
vtable->manager(f.functor, this->functor, clone_functor_tag);
}
template<typename FunctionPtr>
void assign_to(FunctionPtr f, function_ptr_tag)
{
static vtable_base stored_vtable = { &functor_manager_common<FunctionPtr>::manage_ptr };
Clear();
if (f) {
functor.func_ptr = reinterpret_cast<void(*)()>(f);
}
vtable = &stored_vtable;
invoker = &function_invoker1<FunctionPtr, R, T0>::invoke;
}
template<typename FunctionObj>
void assign_to(FunctionObj f, function_obj_tag)
{
static vtable_base stored_vtable = {&functor_manager_common<FunctionObj>::manager};
functor.obj_ptr = new FunctionObj(f);
vtable = &stored_vtable;
invoker=&function_obj_invoker1<FunctionObj, R, T0>::invoke;
}
};
class Test
{
public:
int operator()(int a)
{
return a;
}
static int static_fun(int a)
{
return a;
}
};
int main()
{
Test f_Test;
function<int(int)> f_fun1(f_Test);
function<int(int)> f_fun2(&Test::static_fun);
assert(1 == f_fun1(1));
assert(2 == f_fun2(2));
return 0;
}
我们把T0换成T0,T1,这样我们就可以实现二参函数,以此类推,但是复制代码是原罪,这里大概说下boot的实现方法,
我们一般写头文件时,要求要么加#pragma once,要么使用宏隔开,防止头文件重复包含。此刻,我们需要利用头文件的重复包含,定义function_template.hpp文件,但是不使用宏保护,然后在function1中定义下列宏
#define BOOST_FUNCTION_FUNCTION_INVOKER ...
#define BOOST_FUNCTION_FUNCTION_OBJ_INVOKER ...
#define BOOST_FUNCTION_FUNCTION_REF_INVOKER ...
...
#include <function_template.hpp>
#undef BOOST_FUNCTION_FUNCTION_INVOKER
#undef BOOST_FUNCTION_FUNCTION_OBJ_INVOKER
#undef BOOST_FUNCTION_FUNCTION_REF_INVOKER
...
这样就可以利用同一份代码实现不同参数的function。当然,boot库使用了宏递归来实现的上述参数定义,就不再赘述,这个不是function的重点。
以上基本实现了一个function的缩水版本,理解了上面简单的代码,看boost源码就容易多了。