实现一个简单回调列表

前面我们实现了简单的std::function,本节我们在之前的基础上实现一个可以存储任意回调函数的类,类似观察者模式,调用所有注册到被观察者的回调函数;类似Qt中信号槽,信号触发,调用所有连接到此信号的槽函数(不考虑异步);


我们先用std::function的方式来实现一个,接下来再将std::function替换为我们自己的函数包装类

#include 
#include 

// 原型
template<typename Signature>
class Event;

// 特化
template<typename ReturnType, typename... Args>
class Event<ReturnType(Args...)>
{
private:
    using return_type = ReturnType;
    using function_type = ReturnType(Args...);
    using stl_function_type = std::function<function_type>;
    using pointer = ReturnType(*)(Args...);

public:
    void operator += (stl_function_type func)
    {
        if (func != nullptr)
        {
            m_funcLst.push_back(std::move(func));
        }
    }

    void operator() (Args ...args)
    {
        for (int i = 0; i < m_funcLst.size(); ++i)
        {
            if (m_funcLst[i] != nullptr)
            {
                m_funcLst[i](args...);
            }
        }
    }

private:
    std::vector<stl_function_type> m_funcLst;
};
    Event<void(const std::string &, const bool)> event;
    X x;
    event += std::bind(&X::func, &x, std::placeholders::_1, std::placeholders::_2);
    event += &func;
    event += [](const std::string & s, const bool a){ std::cout << "lambda:" << s << a << std::endl; };
    event("std", true);

以上这个是使用C++11实现的一个回调函数列表类,借助了std::function来包装任意可调用对象


如果不借助std::function,需要怎样自行实现呢?

前面我们实现过一个简单的std::function类,
内部的实现主要是一个非模板接口类 ICallable,派生出 可以包装不同可调用对象的模板类实现这个接口,然后在function包装类中申明 ICallable接口类指针


我们这里也是使用一样的形式,只不过没有function这个包装类后,ICallable这个类就即应该是模板,也应该是接口,同样使用派生的方式,让不同的模板子类来实现这个接口

template<typename Signature>
struct ICallable;

template<typename R, typename... Args>
struct ICallable<R(Args...)>
{
    virtual R invoke(Args&&... args) = 0;
    virtual  ~ICallable() = default;
};

有了以上的签名形式,接下来就需要不同的子类来实现这个接口,现在我们分别写出,包装普通函数和包装类成员函数的子类

//-------------------------------------------------------------------------
template<typename R, typename... Args>
struct NormalCallable : public ICallable<R(Args...)>
{
    using pFunc = R(*)(Args...);
    NormalCallable(pFunc p) : m_p(p)
    {

    }

    virtual R invoke(Args&&... args) override
    {
        if (m_p != nullptr)
        {
            return m_p(std::forward<Args>(args)...);
        }
    }

    pFunc m_p = nullptr;
};

//-------------------------------------------------------------------------
template<typename Class, typename R, typename... Args>
struct MemCallback : public ICallable<R(Args...)>
{
    using pMemFunc = R(Class::*)(Args...);
    MemCallback(Class* obj, pMemFunc p) : _obj(obj), _p(p)
    {

    }

    virtual R invoke(Args&&... args)
    {
        if (_obj != nullptr && _p != nullptr)
        {
            return (_obj->*_p)(std::forward<Args>(args)...);
        }
    }

    Class* _obj = nullptr;
    pMemFunc _p = nullptr;
};

这两个子类,分别实现了对普通函数和类成员函数的包装,由于定义模板类对象必须要指明模板参数,接下来我们可以实现两个辅助函数,用来推导模板参数

//-------------------------------------------------------------------------
//辅助函数
template<typename R, typename... Args>
std::unique_ptr<ICallable<R(Args...)>> make_delegate(R(*p)(Args...))
{
    return std::make_unique<NormalCallable<R, Args...>>(p);
}

template<typename Class, typename R, typename... Args>
std::unique_ptr<ICallable<R(Args...)>> make_delegate(Class* obj, R(Class::*p)(Args...))
{
   return std::make_unique<MemCallback<Class, R, Args...>>(obj, p);
}

到这里,已经实现了使用make_delegate函数来包装普通函数以及类成员函数的功能,接下来我们再写一个容器类,可以将所有函数存储起来,依次调用

//-------------------------------------------------------------------------
template<typename Signature>
struct Callbacks;

template<typename R, typename... Args>
struct Callbacks<R(Args...)>
{
    using CallbackFunc = std::unique_ptr<ICallable<R(Args...)>>;
public:
    void operator +=(CallbackFunc callback)
    {
        m_lst.emplace_back(std::forward<CallbackFunc>(callback));
    }

    void operator()(Args&&... args)
    {
        for (auto& cb : m_lst)
        {
            cb->invoke(std::forward<Args>(args)...);
        }
    }

private:
    std::vector<CallbackFunc> m_lst;
};

这个类非常简单,只是一个可调用函数的容器,重载了()操作符,依次调用注册进来的可调用函数
接下来我们看一看怎样使用:

void func(const std::string & s, const bool a)
{
    std::cout << "----normal func:" << s << a << std::endl;
}

struct X
{
    void func(const std::string & s, const bool a)
    {
        std::cout << "mem func:" << s << a << std::endl;
    }
};

int main(int argc, char *argv[])
{
    Callbacks<void(const std::string &, const bool)> cbs;
    X x;
    cbs += make_delegate(&x, &X::func);
    
    cbs += make_delegate(&func);
    
    cbs("test", false);
    
    return 0;
}

以上这个类,其实就和duilib中的CEventSource类非常相似,其实也是模仿duilib写的,只不过duilib中固定了参数类型为void*,返回值类型为bool,我们这里没有做任何限制;

这个类目前还无法包装lambda表达式函数对象,怎样做才能实现对这两种类型的包装呢,我们后续再讲~

你可能感兴趣的:(模板入门,c++,学习)