观察者(模板)的一点体会

模板写的不多,需要的时候都是网上找找然后改改。对一些概念体会也不深。

正好解决了一个也不能说是问题,算是一个需求吧:让观察者模板在给观察者发消息时,参数让编译器来推导。(之前是写死了)

这个模板,可以接受函数、lambda以及静态类函数作为观察者。

template <typename Func>
class Events {
public:
    /// 注册观察者,支持右值引用
    int Connect(Func&& f, const std::wstring& prompt=L"", int groupType = EventsPriorityEnum::Group1) {
        return Assign(std::forward<Func>(f), groupType, prompt); //这里虽然用了forward,但由于此Connect的参数并不是进行直接推导的,所以f已经是左值了
    }

    /// 注册观察者,左值
    int Connect(const Func& f, const std::wstring& prompt = L"", int groupType = EventsPriorityEnum::Group1) {
        return Assign(f, groupType, prompt);
    }

    /// 移除观察者
    void Disconnect(int key, const std::wstring& prompt = L"", int groupType = EventsPriorityEnum::Group1) {
        m_connections.erase(std::make_tuple(key, groupType,prompt));
    }

    void Clear()
    {
        m_connections.clear();
    }

    //template <typename ... Args>
    //void Notify(Args&& ... args) {
    //    for (auto &it : m_connections) {
    //        auto& key = it.first;
    //        auto& func = it.second;
    //        if(func(std::forward<Args>(args)...))
    //        {
    //            std::string info = fmt::format("{}执行出错", StringUtil::ws2s(std::get<2>(key)));
    //            PromptInfoMgr::GetInstance().Info(info);
    //        }
    //    }
    //}
    
    void Notify(boost::any data) {
        for (auto &it : m_connections) {
            auto& key = it.first;
            auto& func = it.second;
            if (!func(data))
            {
                std::string info = fmt::format("{}执行出错", StringUtil::ws2s(std::get<2>(key)));
                PromptInfoMgr::GetInstance().Info(info);
                //PromptInfoMgr::GetInstance().Info(info);
            }
        }
    }
private:
    /// 保存观察者并分配观察者编号,目前没什么用。没有考虑非常精细得来控制观察者的执行,只按优先级规定的顺序来执行即可
    //template <typename F>
    int Assign(Func&& f, int groupType = EventsPriorityEnum::Group1, const std::wstring& prompt = L"") {
        int k = m_observerId++;
        m_connections.emplace(std::make_tuple(groupType,k,prompt), std::forward<Func>(f));
        return k;
    }

    int m_observerId = 0; //观察者对应编号
    std::map<std::tuple<int/*gorup*/,int/*ObserverId*/,std::wstring>, Func> m_connections; // 观察者列表
};


调用,注册一个lambda:
Events<std::function<void(boost::any)>> events;
events.Connect([](boost::any){
std::cout<<"我被调用了"<<std::endl;
});
events.Notify(0);

但后来发现有个问题,如果我用Events<std::function<void(int,int)>>进行实例化,在调用Notify的时候,编译就有问题了。因为void(int,int)是匹配不上boost::any的。

所以,对于Notify,还是要使用变参数列表:

template <typename ... Args>
void Notify(Args&& ... args) {
    for (auto &it : m_connections) {
        auto& key = it.first;
        auto& func = it.second;
        if(func(std::forward<Args>(args)...))
        {
            std::string info = fmt::format("{}执行出错", StringUtil::ws2s(std::get<2>(key)));
            PromptInfoMgr::GetInstance().Info(info);
        }
    }
}


调用:
Events<std::function<bool(int,int)>> events;
events.Connect([](int x,int y){
std::cout<<"我被调用了"<<std::endl;
return true;
});
events.Notify(1,2);

变参数版本。注意Notify函数的实例化时机:
在用具体类型(比如std::function)对Events的typename Func在定义时,其实已经把函数的参数和返回值都定义好了。比如想记录函数bool fun(int x, int y),Func可以定义为std::function<bool(int, int)>。


Func要调用的参数,虽然在std::function里定义,但个数未知。不能用boost::any来承接,这样的话就是假设只有一个参数了。
所以这里要用变参数进行调用,让编译器进行推导。这样的话就把实例化的任务交到了实例化Notify的的各个调用处(比如dll)。


当编译器看到Events<std::function<bool(int, int)>>时,它只把除了Notify之外的函数给实例化了。而且其实已经把调用的规则给定了下来,
所以在调用Notify的时候也得按照这个规则来调用,否则就会推导失败。在调用Notify的地方,又对Notify进行了实例化。
只是Notify此时还没有实体(在HchxKernel编译的时候)。
这样的化,Events就会有多个Notify的版本,散落在各个模块(实例化它的地方)

相关推荐

  1. 观察模板体会

    2023-12-28 22:38:04       50 阅读
  2. C# 基于事件观察模式

    2023-12-28 22:38:04       50 阅读
  3. 观察模式理解

    2023-12-28 22:38:04       32 阅读
  4. C++中观察模式

    2023-12-28 22:38:04       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-28 22:38:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-28 22:38:04       100 阅读
  3. 在Django里面运行非项目文件

    2023-12-28 22:38:04       82 阅读
  4. Python语言-面向对象

    2023-12-28 22:38:04       91 阅读

热门阅读

  1. 大数据知识分享:大数据产业必知概念

    2023-12-28 22:38:04       63 阅读
  2. SQL备忘--子查询与ALL/ANY运算符

    2023-12-28 22:38:04       57 阅读
  3. 幸运树。。

    2023-12-28 22:38:04       53 阅读
  4. Oracle中varchar2和nvarchar2的区别

    2023-12-28 22:38:04       57 阅读
  5. Qt中保存和还原Widget状态的入门指南

    2023-12-28 22:38:04       68 阅读
  6. Python 虚拟环境工具及使用总结

    2023-12-28 22:38:04       49 阅读
  7. C语言中的Strict Aliasing Rule

    2023-12-28 22:38:04       57 阅读
  8. Python常用命令

    2023-12-28 22:38:04       58 阅读
  9. 面试官:BIO、NIO、AIO的区别

    2023-12-28 22:38:04       52 阅读