让C++回调函数可以同时支持成员函数和静态函数的方法

回调函数是分层软件系统中经常使用的一种技巧,当下层需要调用上层的接口时,一般都使用回调函数来破除循环依赖。在纯C语言环境中,回调函数用起来很简单——一个函数指针而已。但是当大家都用上C++以后,回调函数碰到了点麻烦:很多模块接口都是成员函数,怎么回调?

一些较有经验的程序员会说:成员函数也是可以取函数指针的。没错,但是如果当一个事件触发时,需要依次调用很多个模块的接口呢?(这个场景在“观察者”设计模式中是很常见的)你还能用成员指针么?为了解决这个问题,有人把this指针作为回调函数的一个参数传递,但需要把指针强制转换为void*类型——这种操作一旦碰上多重继承会产生令人头疼的问题(参见我的另一篇博客:《万恶的void*指针类型转换》,我向来都反对滥用void指针)。还有人把回调的函数写成类静态成员函数——这在操作类内部数据上存在诸多不便。

那有没有完美的解决办法?C++有个强大的工具——模板,可以用来简化这个问题。不管是成员函数、静态成员函数、全局函数,只要它们的参数相同,我们都可以看成是一类函数指针。有人很快会产生疑问:成员函数事实上多一个this指针参数,它和全局函数参数个数是不同的。没错,但别忘了STL中有个东东叫函数适配器,它可以把2个参数的函数改造成1个参数。例如:

bind1st(less<int>(), 10));

less<int>是一个二元函数,用来判断第一个参数是否比第二个小。bind1st会生成一个一元函数,让参数x进行less<int>(10, x)的调用。就像它的名字一样,bind1st会把第一个参数“绑死”,从而让二元函数变身为一元函数。

有了这个工具,我们就可以把成员函数的第一个参数——this指针“绑死”,从而也把它改造成和全局/静态函数一样了!

但接下来还有个技术问题:要依次的调用各种函数,就需要用一个容器把这些函数对象给装起来,需要所有这些函数有共同的基类。虽然STL中确实有这种基类——unary_function,但这个类并没有合适的虚函数可以让子类执行操作。所以我们还必须自己定义一个基类来将operator()处理成虚函数。

下面我就给一个例子,在这段代码中,多个模块都可以注册一个函数指针来响应timeup事件。当timeup事件发生时,通知者会循环遍历调用所有需要响应的函数,无论你是成员函数、const成员函数、静态成员函数、全局函数……全部都可以一视同仁的接收同样的通知!小伙伴们再也不用操心该死的this指针啦!

#include <iostream>
#include <functional>
#include <list>

using namespace std;

// 通知结构体,包含通知的消息
class timeup_notify_obj
{
public:
    int cur_time;
};

// 函数对象基类,包含纯虚的operator()接口待子类实现。
class timeup_notify_func : public unary_function<timeup_notify_obj, int>
{
public:
    virtual result_type operator()(const argument_type& notify_arg) const = 0;
};

// 成员函数对象,其中保存有注册时的对象指针,以便调用。
template<typename classname>
class timeup_notify_memfunc : public timeup_notify_func
{
public:
    timeup_notify_memfunc(classname& in_callobj, result_type (classname::*in_func)(argument_type)) 
        : func(bind1st(mem_fun(in_func), &in_callobj))
    {
    }
    virtual result_type operator()(const argument_type& notify_arg) const
    {
        return func(notify_arg);
    }
private:
    binder1st<mem_fun1_t<result_type, classname, argument_type> > func;
};

// const成员函数对象,和成员函数对象唯一的区别是其this指针为const。
template<typename classname>
class timeup_notify_memfunc_const : public timeup_notify_func
{
public:
    timeup_notify_memfunc_const(const classname& in_callobj, result_type (classname::*in_func)(argument_type) const)
        : func(bind1st(mem_fun(in_func), &in_callobj))
    {
    }
    virtual result_type operator()(const argument_type& notify_arg) const
    {
        return func(notify_arg);
    }
private:
    binder1st<const_mem_fun1_t<result_type, classname, argument_type> > func;
};

// 全局/静态函数对象。
class timeup_notify_static_func : public timeup_notify_func
{
public:
    timeup_notify_static_func(result_type (*in_func)(argument_type)) : func(ptr_fun(in_func))
    {
    }
    virtual result_type operator()(const argument_type& notify_arg) const
    {
        return func(notify_arg);
    }
private:
    pointer_to_unary_function<argument_type, result_type> func;
};

// 下面是一些辅助函数,用来生成对应类型的函数对象。
template<typename classname>
inline timeup_notify_memfunc<classname>* creat_timeup_func(classname& in_callobj, int(classname::*in_func)(timeup_notify_obj))
{
    return new timeup_notify_memfunc<classname>(in_callobj, in_func);
}

template<typename classname>
inline timeup_notify_memfunc_const<classname>* creat_timeup_func(const classname& in_callobj,int (classname::*in_func)(timeup_notify_obj) const)
{
    return new timeup_notify_memfunc_const<classname>(in_callobj, in_func);
}

inline timeup_notify_static_func* creat_timeup_func(int (*in_func)(timeup_notify_obj))
{
    return new timeup_notify_static_func(in_func);
}

class BaseA
{
public:
    virtual void func_BaseA(){}
};

class BaseB
{
public:
    virtual void func_BaseB(){}
};

// 这里故意用一个多重继承,来体现其相对于void*指针会更安全。
class A : public BaseA, public BaseB
{
public:
    virtual int update(timeup_notify_obj timeup_obj)
    {
        return timeup_obj.cur_time + my_time;
    }

    int update_const(timeup_notify_obj timeup_obj) const
    {
        return timeup_obj.cur_time * my_time;
    }

    static int update_static(timeup_notify_obj timeup_obj)
    {
        return timeup_obj.cur_time;
    }

    int my_time;
};

int update_global(timeup_notify_obj timeup_obj)
{
    return timeup_obj.cur_time + 100;
}

int main(int argc, _TCHAR* argv[])
{
    list<timeup_notify_func*> notify_func_list;
    A a;
    a.my_time = 2;
    notify_func_list.push_back(creat_timeup_func(a, &A::update));
    A b;
    b.my_time = 5;
    notify_func_list.push_back(creat_timeup_func(b, &A::update_const));
    notify_func_list.push_back(creat_timeup_func(A::update_static));
    notify_func_list.push_back(creat_timeup_func(update_global));

    timeup_notify_obj timeup;
    timeup.cur_time = 4;

    // 循环遍历调用所有类型的函数
    for (list<timeup_notify_func*>::iterator it = notify_func_list.begin(); it !=notify_func_list.end(); ++it)
    {
        int ret = (*(*it))(timeup);
        cout << ret << endl;
    }

    return 0;
}
代码运行结果:

6
20
4
104

上面这个例子中的timeup_notify_func函数对象其实具有很强的扩展性,你可以轻松的把它做成一个支持所有类型事件的模板,而不仅仅是只能用于timeup事件。只要把timeup_notify_obj这个事件类型给参数化就可以了。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值