随笔-90  评论-947  文章-0  trackbacks-0

目录:

C++ 下 Function 对象的实现(上)
C++ 下 Function 对象的实现(下)

上篇中,我们实现了一个支持 R () 型函数的 Function。补充说明一下,在我们对成员函数的支持中,我们是这样定义的:

template <typename R, typename T>
class MemberFunction0 : public FunctionBase0<R>
{

private:
    R (T::*m_pMemFun)();
    T *m_pObj;
};

Loki 特意在著作中提醒我们,这里的 T 最好不要是函数类型,改为函数指针类型,如此该类的支持范围将扩大。如下:

template <typename R, typename P, typename T>
class MemberFunction0 : public FunctionBase0<R>
{
public:
    R Invoke()
    {
        return (m_pObj->*m_pMemFun)();
    }

public:
    MemberFunction0(P pObj, R (T::*pMemFun)())
        : m_pObj(pObj), m_pMemFun(pMemFun)
    {

    }

private:
    R (T::*m_pMemFun)();
    P m_pObj;
};

于是,P 和 T 的关系不那么紧密了,P 不一定非要 T* 不可,也可以是诸如 SmartPtr<T> 之类的玩意儿。原本只支持传入一个对象和该对象的成员函数的,现在变成传入一个具有指针概念的东东和一个成员函数,只要这个“指针”使用运算符 –> 去调用那个成员函数合乎语法即可。

接下来,我们来扩展这个 Function,以支持拥有数目在给定上限内的任意参数的函数。

我们先来手工写一下,看看如何支持带一个参数的函数。首先定义一个虚基类:

template <typename R, typename T0>
class FunctionBase1
{
public:
    virtual R Invoke(T0) = 0;
    virtual ~FunctionBase1() {}
};

实现两个版本,分别支持非成员函数和成员函数:

template <typename R, typename T0, typename T>
class Function1 : public FunctionBase1<R, T0>
{
public:
    R Invoke(T0 v0)
    {
        return m_Fun(v0);
    }

public:
    Function1(const T &fun)
        : m_Fun(fun)
    {

    }

private:
    T m_Fun;
};

template <typename R, typename P, typename T, typename T0>
class MemberFunction1 : public FunctionBase1<R, T0>
{
public:
    R Invoke(T0 v0)
    {
        return (m_pObj->*m_pMemFun)(v0);
    }

public:
    MemberFunction1(P pObj, R (T::*pMemFun)(T0))
        : m_pObj(pObj), m_pMemFun(pMemFun)
    {

    }

private:
    R (T::*m_pMemFun)(T0);
    P m_pObj;
};

增加一个函数引用萃取的偏特化版本:

template <typename RetType, typename T0>
struct FunctionTraits<RetType (T0)>
{
    typedef RetType (&ParamType)(T0);
};

增加一个 Function 类的偏特化版本:

template <typename R, typename T0>
class Function<R (T0)>
{
public:
    template <typename T>
    Function(const T &fun)
        : m_pFunBase(new Function1<R, T0, typename FunctionTraits<T>::ParamType>(fun))
    {
       
    }

    template <typename P, typename T>
    Function(P pObj, R (T::*pMemFun)(T0))
        : m_pFunBase(new MemberFunction1<R, P, T, T0>(pObj, pMemFun))
    {

    }

    ~Function()
    {
        delete m_pFunBase;
    }

    R operator ()(T0 v0)
    {
        return m_pFunBase->Invoke(v0);
    }

private:
    FunctionBase1<R, T0> *m_pFunBase;
};

现在,我们可以跑一下测试代码了:

Function<int (int)> f1(&intfun1);
Function<int (int)> f1_(intfun1);
Function<int (int)> f2(intfunctor1);
Function<int (int)> f3(&test, &Test::intmem1);

f1(1);
f1_(1);
f2(2);
f3(3);

当然,void 函数也是支持的。

观察上面的这些代码,和我们在上一篇中的代码高度一致,不同的是那些模版参数、偏特化参数、函数调用参数等地方。

假如有这么一组宏:
TYPENAME_DECLARE(n) 被定义为 typename T0, typename T1, …, typename Tn
TYPENAME_LIST(n) 被定义为 T0, T1, …, Tn
TYPENAME_VARIABLE(n) 被定义为 T0 v0, T1 v1, …, Tn vn
VARIABLE_LIST(n) 被定义为 v0, v1, …, vn

那么我们可以使用一个 n 就写出支持所有具有参数的函数的 Function 了。我们抛弃掉上面的 1 系列的所有类,仅保持上篇留下来的代码,然后利用上面 4 个宏将所有数字尾巴去掉,于是代码变成:

template <typename R, TYPENAME_DECLARE(n)>
class FunctionBase_##n
{
public:
    virtual R Invoke(TYPENAME_LIST(n)) = 0;
    virtual ~FunctionBase_##n() {}
};


template <typename R, TYPENAME_DECLARE(n), typename T>
class Function_##n : public FunctionBase_##n<R, TYPENAME_LIST(n)>
{
public:
    R Invoke(TYPENAME_VARIABLE(n))
    {
        return m_Fun(VARIABLE_LIST(n));
    }

public:
    Function_##n(const T &fun)
        : m_Fun(fun)
    {

    }

private:
    T m_Fun;
};

template <typename R, typename P, typename T, TYPENAME_DECLARE(n)>
class MemberFunction_##n : public FunctionBase_##n<R, TYPENAME_LIST(n)>
{
public:
    R Invoke(TYPENAME_VARIABLE(n))
    {
        return (m_pObj->*m_pMemFun)(VARIABLE_LIST(n));
    }

public:
    MemberFunction_##n(P pObj, R (T::*pMemFun)(TYPENAME_LIST(n)))
        : m_pObj(pObj), m_pMemFun(pMemFun)
    {

    }

private:
    R (T::*m_pMemFun)(TYPENAME_LIST(n));
    P m_pObj;
};

template <typename RetType, TYPENAME_DECLARE(n)>
struct FunctionTraits<RetType (TYPENAME_LIST(n))>
{
    typedef RetType (&ParamType)(TYPENAME_LIST(n));
};

template <typename R, TYPENAME_DECLARE(n)>
class Function<R (TYPENAME_LIST(n))>
{
public:
    template <typename T>
    Function(const T &fun)
        : m_pFunBase(new Function_##n<R, TYPENAME_LIST(n), typename FunctionTraits<T>::ParamType>(fun))
    {
       
    }

    template <typename P, typename T>
    Function(P pObj, R (T::*pMemFun)(TYPENAME_LIST(n)))
        : m_pFunBase(new MemberFunction_##n<R, P, T, TYPENAME_LIST(n)>(pObj, pMemFun))
    {

    }

    ~Function()
    {
        delete m_pFunBase;
    }

    R operator ()(TYPENAME_VARIABLE(n))
    {
        return m_pFunBase->Invoke(VARIABLE_LIST(n));
    }

private:
    FunctionBase_##n<R, TYPENAME_LIST(n)> *m_pFunBase;
};

当然上面这样子的代码是没法跑的咯。如果我们将整段代码定义为一个宏 BODY(n),然后用类似刚才四个宏的方式定义宏 FUNCTION_IMPLEMENT(n),使得它的含义为 BODY(0), BODY(1), …, BODY(n),所有工作就都完成了。最后只需要丢下一句 FUNCTION_IMPLEMENT(20),就可以支持 0 到 21 个参数了。

最后归结为,如何使用宏搞出“T0, T1, …, Tn” 的形式。

暴力点,我们可以这样:

#define T_0 T0
#define T_1 T_0, T1
#define T_2 T_1, T2
#define T_3 T_2, T3
#define T_4 T_3, T4
#define T_5 T_4, T5
#define T_6 T_5, T6
#define T_7 T_6, T7
#define T_8 T_7, T8
#define T_9 T_8, T9

#define T(n) T_##n

这样子,对于上面四个宏可以,但是对于最后的 X(n),人工代码量还是太大了。嗯?X(n)?对,这个 X,必须在 _1、_2、_3 系列宏里面占据一个参数地位,这样才有那么一点点扩展性。考虑换成这样:

#define REP_0(macro, n) macro(0)
#define REP_1(macro, n) REP_0(macro, n), macro(1)
#define REP_2(macro, n) REP_1(macro, n), macro(2)
#define REP_3(macro, n) REP_2(macro, n), macro(3)
#define REP_4(macro, n) REP_3(macro, n), macro(4)
#define REP_5(macro, n) REP_4(macro, n), macro(5)
#define REP_6(macro, n) REP_5(macro, n), macro(6)
#define REP_7(macro, n) REP_6(macro, n), macro(7)
#define REP_8(macro, n) REP_7(macro, n), macro(8)
#define REP_9(macro, n) REP_8(macro, n), macro(9)

#define REP(macro, n)   REP_##n(macro, n)

然后:

#define TYPENAME_LIST_PATTERN(n)    T##n
#define TYPENAME_LIST(n)            REP(TYPENAME_LIST_PATTERN, n)

这个 TYPENAME_LIST 就是符合上文要求的宏。接下来如法炮制其余三个:

#define TYPENAME_DECLARE_PATTERN(n)     typename T##n
#define TYPENAME_DECLARE(n)             REP(TYPENAME_DECLARE_PATTERN, n)

#define TYPENAME_VARIABLE_PATTERN(n)    T##n v##n
#define TYPENAME_VARIABLE(n)            REP(TYPENAME_VARIABLE_PATTERN, n)

#define VARIABLE_LIST_PATTERN(n)        v##n
#define VARIABLE_LIST(n)                REP(VARIABLE_LIST_PATTERN, n)

最后,我们在 #define FUNCTION_IMPLEMENT(n)  REP(BODY, n) 中还存在一点点问题。因为 BODY 中会含有 TYPENAME_DECLARE 之类的宏的使用,而 TYPENAME_DECLARE 正是使用 REP 定义的。这涉及到宏的递归展开,C++预处理器的规则是,遇到这样的情况就停止展开。比如,我们 定义 BODY(n) 为 TYPENAME_DECLARE(n),于是 FUNCTION_IMPLEMENT(2) 会被展成:

REP(TYPENAME_DECLARE_PATTERN, 0), REP(TYPENAME_DECLARE_PATTERN, 1), REP(TYPENAME_DECLARE_PATTERN, 2)

上面的 REP 不会被继续展开了。

为此,一个不太聪明的办法就是,再定义一组 REP2。嗯,是个办法,就这么办吧。另外我们刚才的 REP 系列没有将分隔符作为参数,默认使用逗号,而最后一不的 FUNCTION_IMPLEMENT 的重复中是不能用逗号的。考虑提取出来作为参数。最后我们的所需要的宏系统是:

#define NIL
#define COMMA ,

#define REP_0(macro, splitter, n) macro(0)
#define REP_1(macro, splitter, n) REP_0(macro, splitter, n) splitter macro(1)
#define REP_2(macro, splitter, n) REP_1(macro, splitter, n) splitter macro(2)
#define REP_3(macro, splitter, n) REP_2(macro, splitter, n) splitter macro(3)
#define REP_4(macro, splitter, n) REP_3(macro, splitter, n) splitter macro(4)
#define REP_5(macro, splitter, n) REP_4(macro, splitter, n) splitter macro(5)
#define REP_6(macro, splitter, n) REP_5(macro, splitter, n) splitter macro(6)
#define REP_7(macro, splitter, n) REP_6(macro, splitter, n) splitter macro(7)
#define REP_8(macro, splitter, n) REP_7(macro, splitter, n) splitter macro(8)
#define REP_9(macro, splitter, n) REP_8(macro, splitter, n) splitter macro(9)

#define REP(macro, splitter, n)   REP_##n(macro, splitter, n)

#define REP2_0(macro, splitter, n) macro(0)
#define REP2_1(macro, splitter, n) REP2_0(macro, splitter, n) splitter macro(1)
#define REP2_2(macro, splitter, n) REP2_1(macro, splitter, n) splitter macro(2)
#define REP2_3(macro, splitter, n) REP2_2(macro, splitter, n) splitter macro(3)
#define REP2_4(macro, splitter, n) REP2_3(macro, splitter, n) splitter macro(4)
#define REP2_5(macro, splitter, n) REP2_4(macro, splitter, n) splitter macro(5)
#define REP2_6(macro, splitter, n) REP2_5(macro, splitter, n) splitter macro(6)
#define REP2_7(macro, splitter, n) REP2_6(macro, splitter, n) splitter macro(7)
#define REP2_8(macro, splitter, n) REP2_7(macro, splitter, n) splitter macro(8)
#define REP2_9(macro, splitter, n) REP2_8(macro, splitter, n) splitter macro(9)

#define REP2(macro, splitter, n)   REP2_##n(macro, splitter, n)

#define TYPENAME_DECLARE_PATTERN(n)     typename T##n
#define TYPENAME_DECLARE(n)             REP(TYPENAME_DECLARE_PATTERN, COMMA, n)

#define TYPENAME_LIST_PATTERN(n)        T##n
#define TYPENAME_LIST(n)                REP(TYPENAME_LIST_PATTERN, COMMA, n)

#define TYPENAME_VARIABLE_PATTERN(n)    T##n v##n
#define TYPENAME_VARIABLE(n)            REP(TYPENAME_VARIABLE_PATTERN, COMMA, n)

#define VARIABLE_LIST_PATTERN(n)        v##n
#define VARIABLE_LIST(n)                REP(VARIABLE_LIST_PATTERN, COMMA, n)

#define FUNCTION_IMPLEMENT(n)  REP2(BODY, NIL, n)

最后,定义一下 FUNCTION_IMPLEMENT(5),就可以支持到 6 个参数了。为了支持更多参数,把上面的 REP 以及 REP2 系列多定义一点,比如到 50,那么 FUNCTION_IMPLEMENT 的括号中就可以填 50 以内的任意数了。考虑到宏展开对编译速度的影响,以及实际应用中函数参数的个数,定为 20 左右比较合适。

到这里,我们的Function已经实现了预期目标。接下来我本来想说说 TypeList 的。可是现在发现没有 TypeList,Function 跑的通;有了 TypeList,Function 也不能写的漂亮多少,虽说那些重复部分有一定的减少。Loki 的 Functor 的参数类型是一个返回值类型加上一个 TypeList,是由用户直接传入 TypeList 的,不用由散的类型组合出一个TypeList(但还是要从TypeList中萃取各个参数类型),因此用在他那里看上去美妙一点点。当然,Loki 也在 Functor 外头包了一层 Function,以支持函数签名作为模版参数的使用方式。有一点不算改观的改观是,用了 TypeList 以后,就不会再有 FunctionBase_1, FunctionBase_2 这样的玩意儿了,取而代之的是一个统一的 FunctionBase 外加许多偏特化版本,Function* 和 MemberFunction* 可以分别统一为一个,但是每一个里头都需要实现 N 个 Invoke。加上篇幅关系,我想这里就不说这个 TypeList 了。

代码清单太长了,就不贴了,有意者自然能凑起来。我目前在 xlLib 中的最终实现见 xlFunction.h

关于宏,我不知道可以怎样改进。BOOST_PP_REPEAT 貌似可以调用自身?不知道如何实现的,求指教。另外@vczh貌似说“实现了一门可以自己递归自己和内置列表处理的另一个宏”,求分享呀求分享。

2010-01-18 补充:将最外层 Function 的构造函数中的 const T & 直接改为 T,并且抛弃 FunctionTraits,函数实体类型将在传递过程中直接退化为函数指针,这样就能特化出正确的 FunctionHandler。同时带来另一点影响:如果传入 Functor,字面上将多一次拷贝动作。抛开这一点微小的性能来讲,这比刚才的 FunctionTraints 要好得多了。

posted on 2011-01-17 21:59 溪流 阅读(4118) 评论(5)  编辑 收藏 引用 所属分类: C++

评论:
# re: C++ 下 Function 对象的实现(下) 2011-01-19 16:42 | zhaoyg
你好,我发现了个问题:
对于:
R (T::*m_pMemFun)();
P m_pObj;

当P是一个shared_ptr<...> *时,或许得让m_pObj先解一层(即*m_pObj)后才能这样吧: (m_pObj->*m_pMemFun)(v0),
  回复  更多评论
  
# re: C++ 下 Function 对象的实现(下) 2011-01-19 20:48 | 溪流
@zhaoyg
shared_ptr<X> * 当然得自己先解一层
shared_ptr<X> 才和 X* 具有形式上的等价性么   回复  更多评论
  
# re: C++ 下 Function 对象的实现(下) 2011-01-19 22:15 | zhaoyg
明白了明白了,反复看了几遍你写的,总算是理清了一些东西,收获颇多
谢谢你的文字:)  回复  更多评论
  
# re: C++ 下 Function 对象的实现(下) 2011-01-19 23:27 | 陈梓瀚(vczh)
# re: C++ 下 Function 对象的实现(下) 2011-01-20 23:37 | 溪流
@陈梓瀚(vczh)
这是额外一个工具,生成代码后才能贴在 C++ 里的吗?  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理