前言说明

在文中《【学习笔记】开源库之 - sigslot (提供该库存在对象拷贝崩溃问题的解决方案)》已经介绍过 sigslot ,此文主要应用在实际的工作项目中时,发现会有拦截信号的需求,而原生的 sigslot 不支持拦截。因此增加 sigslot 拦截特性,便于适应这种需求。

应用场景

在很多需要联网的项目中,很多功能模块都必不可免的会产生依赖关系,如联网后需要进行网络时间同步、连接服务器等操作。而网络时间同步和连接服务器的操作需要网络畅通时才能进行,而连接服务器需要在网络时间同步成功后才能进行(假设连接服务器需要以正确的时间计算连接秘钥),因此可能会出现以下几种情况:

  • 情况一,如设备只连接了路由器,而路由器却没有连接互联网,这时候进行网络时间同步和连接服务器等操作就会非常耗时,且注定不会成功。
  • 情况二,已经连接了互联网,但是网络时间同步失败,连接服务器又需要当前准确的时间来计算秘钥,这时候连接服务器的操作就会失败。

以WIFI联网方式为例,根据 sigslot 的设计,信号通知槽函数的顺序与连接信号的顺序一致,因此我们可以将WIFI连接成功的信号,连接到三个不同模块的槽函数,第一个是网络处理的模块,负责进一步验证是否已经连接到互联网,第二个是时间同步模块,负责同步网络时间,第三个是服务器模块,负责与服务器进行通信。那么当网络连接成功的信号发出,会按照顺序先触发网络状态模块、再触发时间同步模块,最后触发服务器模块。

收到网络连接成功的事件后,在网络处理模块中,如果无法访问外网,则可以发出提示音提醒用户网络未连接到互联网,借此提升用户体验,然后返回非零值,让 sigslot 停止触发下一个槽函数,如果可以访问外网,就返回零值,继续触发下一个槽函数,即时间同步模块。在时间同步模块中,如果同步时间失败,也是返回非零值,如果同步成功则返回零值,继续触发下一个槽函数,即服务器模块,服务器模块就可以真正畅通无阻的进行与服务器进行连接等相关操作了。

实现方式

要实现如上的设计,需要改造 siglsot ,主要进行如下两步即可:

  • 修改槽函数的返回值类型,由 void 改为 int
  • 修改触发信号的接口 .emit() 、operator()
    • 对每个槽函数的返回值进行判断,非零值则跳出。
    • 返回值由 void 改为 int ,如果槽函数返回值非零,则直接返回该值,否则返回零值。

以上的修改,可以实现,不同的槽函数返回不同的值,这样触发信号的函数就可以通过返回值得知是哪个槽函数拦截了。

以下为 signal0<> 的修改方法:

--- C:\sigslot.h	2020-05-09 00:34:55.000000000 +0800
+++ F:\sigslot.h 2020-05-09 01:41:50.000000000 +0800 @@ -315,13 +315,13 @@
template<class mt_policy>
class _connection_base0
{
public:
virtual ~_connection_base0() {}
virtual has_slots_interface* getdest() const = 0;
- virtual void emit() = 0;
+ virtual int emit() = 0;
virtual _connection_base0* clone() = 0;
virtual _connection_base0* duplicate(has_slots_interface* pnewdest) = 0;
}; template<class arg1_type, class mt_policy>
class _connection_base1
@@ -1779,13 +1779,13 @@
_connection0()
{
m_pobject = NULL;
m_pmemfun = NULL;
} - _connection0(dest_type* pobject, void (dest_type::* pmemfun)())
+ _connection0(dest_type* pobject, int (dest_type::* pmemfun)())
{
m_pobject = pobject;
m_pmemfun = pmemfun;
} virtual ~_connection0()
@@ -1799,25 +1799,25 @@ virtual _connection_base0<mt_policy>* duplicate(has_slots_interface* pnewdest)
{
return new _connection0<dest_type, mt_policy>((dest_type*)pnewdest, m_pmemfun);
} - virtual void emit()
+ virtual int emit()
{
- (m_pobject->*m_pmemfun)();
+ return (m_pobject->*m_pmemfun)();
} virtual has_slots_interface* getdest() const
{
return m_pobject;
} private:
dest_type* m_pobject;
- void (dest_type::* m_pmemfun)();
+ int (dest_type::* m_pmemfun)();
}; template<class dest_type, class arg1_type, class mt_policy>
class _connection1 : public _connection_base1<arg1_type, mt_policy>
{
public:
@@ -2239,53 +2239,63 @@
: _signal_base0<mt_policy>(s)
{
;
} template<class desttype>
- void connect(desttype* pclass, void (desttype::* pmemfun)())
+ void connect(desttype* pclass, int (desttype::* pmemfun)())
{
lock_block<mt_policy> lock(this);
_connection0<desttype, mt_policy>* conn =
new _connection0<desttype, mt_policy>(pclass, pmemfun);
m_connected_slots.push_back(conn);
pclass->signal_connect(this);
} - void emit()
+ int emit()
{
lock_block<mt_policy> lock(this);
typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ int nResult = 0; while (it != itEnd)
{
itNext = it;
++itNext; - (*it)->emit();
+ if (nResult = (*it)->emit()) {
+ return nResult;
+ } it = itNext;
}
+
+ return 0;
} - void operator()()
+ int operator()()
{
lock_block<mt_policy> lock(this);
typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ int nResult = 0; while (it != itEnd)
{
itNext = it;
++itNext; - (*it)->emit();
+ if (nResult = (*it)->emit()) {
+ return nResult;
+ } it = itNext;
}
+
+ return 0;
} // add lmx: https://me.csdn.net/lovemengx -- 2020-04-12
signal0 &operator=(const signal0 &singnal)
{
if (this != &singnal)

测试例程

#include <iostream>
#include <thread>
#include "sigslot.h" using namespace std;
using namespace sigslot; #define iprintf(format,...) printf("[inf] %s ->%s():%05d "format" <--\n", typeid(this).name(), __func__, __LINE__, ##__VA_ARGS__) class SignalSource
{ public: signal0<> TriSignal0;
signal1<string> TriSignal1;
signal2<string, int> TriSignal2;
signal3<string, int, int> TriSignal3;
signal4<string, int, int, int> TriSignal4;
signal5<string, int, int, int, int> TriSignal5;
signal6<string, int, int, int, int, int> TriSignal6;
signal7<string, int, int, int, int, int, int> TriSignal7;
signal8<string, int, int, int, int, int, int, int> TriSignal8; public: void run()
{
iprintf("TriSigna0 Reslut:%d\n", TriSignal0());
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSignal Reslut:%d\n", TriSignal1("TriSignal1"));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna2 Reslut:%d\n", TriSignal2("TriSignal2", 0));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna3 Reslut:%d\n", TriSignal3("TriSignal3", 0, 1));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna4 Reslut:%d\n", TriSignal4("TriSignal4", 0, 1, 2));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna5 Reslut:%d\n", TriSignal5("TriSignal5", 0, 1, 2, 3));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna6 Reslut:%d\n", TriSignal6("TriSignal6", 0, 1, 2, 3, 4));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna7 Reslut:%d\n", TriSignal7("TriSignal7", 0, 1, 2, 3, 4, 5));
this_thread::sleep_for(chrono::seconds(1));
iprintf("TriSigna8 Reslut:%d\n", TriSignal8("TriSignal8", 0, 1, 2, 3, 4, 5, 6));
this_thread::sleep_for(chrono::seconds(1));
}
}; class SignalProcessBase : public has_slots<>
{
public: virtual int onSignal0() { return 0; };
virtual int onSignal1(string) { return 0; };
virtual int onSignal2(string, int) { return 0; };
virtual int onSignal3(string, int, int) { return 0; };
virtual int onSignal4(string, int, int, int) { return 0; };
virtual int onSignal5(string, int, int, int, int) { return 0; };
virtual int onSignal6(string, int, int, int, int, int) { return 0; };
virtual int onSignal7(string, int, int, int, int, int, int) { return 0; };
virtual int onSignal8(string, int, int, int, int, int, int, int) { return 0; }; }; class SignalProcess1 : public SignalProcessBase
{
private: virtual int onSignal0() { iprintf(""); return 0; };
virtual int onSignal1(string) { iprintf(""); return 1; }; // 拦截, 返回1
virtual int onSignal2(string, int) { iprintf(""); return 0; };
virtual int onSignal3(string, int, int) { iprintf(""); return 0; };
virtual int onSignal4(string, int, int, int) { iprintf(""); return 0; };
virtual int onSignal5(string, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal6(string, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal7(string, int, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal8(string, int, int, int, int, int, int, int) { iprintf(""); return 0; };
}; class SignalProcess2 : public SignalProcessBase
{
private: virtual int onSignal0() { iprintf(""); return 0; };
virtual int onSignal1(string) { iprintf(""); return 0; };
virtual int onSignal2(string, int) { iprintf(""); return 2; }; // 拦截,返回2
virtual int onSignal3(string, int, int) { iprintf(""); return 0; };
virtual int onSignal4(string, int, int, int) { iprintf(""); return 0; };
virtual int onSignal5(string, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal6(string, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal7(string, int, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal8(string, int, int, int, int, int, int, int) { iprintf(""); return 0; };
}; class SignalProcess3 : public SignalProcessBase
{
private: virtual int onSignal0() { iprintf(""); return 0; };
virtual int onSignal1(string) { iprintf(""); return 0; };
virtual int onSignal2(string, int) { iprintf(""); return 0; };
virtual int onSignal3(string, int, int) { iprintf(""); return 3; }; // 拦截,返回3
virtual int onSignal4(string, int, int, int) { iprintf(""); return 0; };
virtual int onSignal5(string, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal6(string, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal7(string, int, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal8(string, int, int, int, int, int, int, int) { iprintf(""); return 0; };
}; class SignalManager : public SignalProcessBase
{
private: virtual int onSignal0() { iprintf(""); return 9; }; // 拦截, 返回9
virtual int onSignal1(string) { iprintf(""); return 0; };
virtual int onSignal2(string, int) { iprintf(""); return 0; };
virtual int onSignal3(string, int, int) { iprintf(""); return 0; };
virtual int onSignal4(string, int, int, int) { iprintf(""); return 0; };
virtual int onSignal5(string, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal6(string, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal7(string, int, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal8(string, int, int, int, int, int, int, int) { iprintf(""); return 0; }; private: SignalProcess1 mSignalProcess1;
SignalProcess2 mSignalProcess2;
SignalProcess3 mSignalProcess3; void connectBase(SignalSource* pSignalSource, SignalProcessBase* pSignalProcessBase)
{
pSignalSource->TriSignal0.connect(pSignalProcessBase, &SignalProcessBase::onSignal0);
pSignalSource->TriSignal1.connect(pSignalProcessBase, &SignalProcessBase::onSignal1);
pSignalSource->TriSignal2.connect(pSignalProcessBase, &SignalProcessBase::onSignal2);
pSignalSource->TriSignal3.connect(pSignalProcessBase, &SignalProcessBase::onSignal3);
pSignalSource->TriSignal4.connect(pSignalProcessBase, &SignalProcessBase::onSignal4);
pSignalSource->TriSignal5.connect(pSignalProcessBase, &SignalProcessBase::onSignal5);
pSignalSource->TriSignal6.connect(pSignalProcessBase, &SignalProcessBase::onSignal6);
pSignalSource->TriSignal7.connect(pSignalProcessBase, &SignalProcessBase::onSignal7);
pSignalSource->TriSignal8.connect(pSignalProcessBase, &SignalProcessBase::onSignal8);
} public: void connect(SignalSource *pSignalSource)
{
connectBase(pSignalSource, (SignalProcessBase*)this);
connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess1);
connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess2);
connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess3);
} }; int main()
{
SignalSource mSignalSource;
SignalManager mSignalManager; mSignalManager.connect(&mSignalSource);
mSignalSource.run(); return 0; }

测试结果

可以观察触发信号的返回值,即可确认所有槽函数是否都正常执行,如果某个槽函数返回非零值,即表明有拦截且可以通过返回值即可得知是哪个槽函数拦截的。

如 TirSignal0 , 被第一个槽函数 SignalManager::onSignal0() 拦截(观察返回值为9),那么之后的SignalProcess1::onSignal0()、SignalProcess2::onSignal0()、SignalProcess3::onSignal0() 槽函数都不会被执行。

完整修改文件:https://download.csdn.net/download/lovemengx/12406152

温故知新

在分析代码的时候发现槽函数有重新实现拷贝构造函数:

template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
class has_slots : public has_slots_interface, public mt_policy
{
private:
...... public:
......
has_slots(const has_slots& hs)
{
lock_block<mt_policy> lock(this);
const_iterator it = hs.m_senders.begin();
const_iterator itEnd = hs.m_senders.end(); while (it != itEnd)
{
(*it)->slot_duplicate(&hs, this);
m_senders.insert(*it);
++it;
}
} ...... private:
......
};

这就意味着在,可以通过传入已经有信号和槽的连接关系的对象,来初始化新的对象,使新的对象具有相同的连接关系。这个功能很有用,特别合适在多个不通过的功能模块需要共享相同的多个信号场景,当第一个功能模块已经建立好所有的信号与槽的连接关系,那么其他的功能模块可以使用此模块来构造,即可完成连接关系的拷贝,当需要新增加信号或者移除信号的时候,只需要修改第一个模块的连接关系,即可同步其他模块的信号连接关系。需要注意的是,需要将槽函数抽象为父类,其他模块继承该类才能实现不同功能模块的连接关系同步,可以参考本文的例程。如果是相同的类不同的对象则可以直接使用。

下面的代码通过构造函数,减少了信号连接的操作,也可以实现和上面例程相同的效果。(请忽略下面内存释放问题o(∩_∩)o)

---
+++
@@ -122,15 +122,15 @@
virtual int onSignal7(string, int, int, int, int, int, int) { iprintf(""); return 0; };
virtual int onSignal8(string, int, int, int, int, int, int, int) { iprintf(""); return 0; }; private: - SignalProcess1 mSignalProcess1;
- SignalProcess2 mSignalProcess2;
- SignalProcess3 mSignalProcess3;
+ //SignalProcess1 mSignalProcess1;
+ //SignalProcess2 mSignalProcess2;
+ //SignalProcess3 mSignalProcess3; void connectBase(SignalSource* pSignalSource, SignalProcessBase* pSignalProcessBase)
{
pSignalSource->TriSignal0.connect(pSignalProcessBase, &SignalProcessBase::onSignal0);
pSignalSource->TriSignal1.connect(pSignalProcessBase, &SignalProcessBase::onSignal1);
pSignalSource->TriSignal2.connect(pSignalProcessBase, &SignalProcessBase::onSignal2);
@@ -144,16 +144,21 @@ public: void connect(SignalSource *pSignalSource)
{
+
connectBase(pSignalSource, (SignalProcessBase*)this);
- connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess1);
- connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess2);
- connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess3);
+ //connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess1);
+ //connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess2);
+ //connectBase(pSignalSource, (SignalProcessBase*)&mSignalProcess3);
+
+ SignalProcess1* pSignalProcess1 = new SignalProcess1(*(SignalProcess1*)this);
+ SignalProcess2* pSignalProcess2 = new SignalProcess2(*(SignalProcess2*)pSignalProcess1);
+ SignalProcess3* pSignalProcess3 = new SignalProcess3(*(SignalProcess3*)pSignalProcess2);
} }; int main()
{

【学习笔记】开源库之 - sigslot (在解决浅拷贝问题的基础上增加信号拦截功能)的更多相关文章

  1. Linux学习笔记(7)CRT实现windows与linux的文件上传下载

    Linux学习笔记(7)CRT实现windows与linux的文件上传下载 按下Alt + p 进入SFTP模式,或者右击选项卡进入 命令介绍 help 显示该FTP提供所有的命令 lcd 改变本地上 ...

  2. [Golang学习笔记] 03 库源码文件

    库源码文件:不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用. 代码包声明的基本规则: 1. 同目录下的源码文件的代码包声明语句要一致.也就是说,它们要同属于一个代码包( ...

  3. [原创]java WEB学习笔记34:Session 案例 之 解决表单重复提交

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  4. Dynamic CRM 2013学习笔记(十一)利用Javascript实现子表合计(汇总,求和)功能

    我们经常有这样一种需求,子表里新加或修改一数值后,要马上在主表里把它们的和显示在主表上.如果用插件来实现,可以实现求和,但页面上还要刷新一下才能显示正确.这时就考虑到用JS来实现这一功能,并自动刷新页 ...

  5. MySQL学习笔记:一道group by+group_concat解决的小问题

    闲来无事,逛逛V2EX发现一道MySQL数据库题目,原题如下: 遂打开很长一段时间都没用过SQLyog,噗呲噗呲的干起活来…… 建测试表: CREATE TABLE test_001 ( id INT ...

  6. Docker技术入门与实战 第二版-学习笔记-6-仓库

    仓库(Repository)是集中存放镜像的地方 一个容易混淆的概念是注册服务器(Registry). 实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像. ...

  7. mysql学习笔记1---mysql ERROR 1045 (28000): 错误解决办法(续:深入分析)

    在命令行输入mysql -u root –p,输入密码,或通过工具连接数据库时,经常出现下面的错误信息,详细该错误信息很多人在使用MySQL时都遇到过. ERROR 1045 (28000): Acc ...

  8. 【小梅哥SOPC学习笔记】SOPC开发常见问题及解决办法集锦

    SOPC开发常见问题及解决办法集锦 一.Symbol 'NULL' could not be resolved 近期在评估使用NIOS II处理器进行项目的开发,我使用的软件是Quartus II 1 ...

  9. SharpGL学习笔记(十二) 光源例子:解决光源场景中的常见问题

    笔者学到光源这一节,遇到的问题就比较多了,收集了一些如下所述: (1) 导入的3ds模型,如果没有材质光照效果很奇怪.如下图 (2) 导入的3ds模型,有材质,灯光效果发暗,材质偏色,效果也很奇怪. ...

  10. poco网络库分析,教你如何学习使用开源库

    Poco::Net库中有 FTPClient HTML HTTP HTTPClient HTTPServer ICMP Logging Mail Messages NetCore NTP OAuth ...

随机推荐

  1. 学习Rust第一天 Rust语言特点

    学习Rust之前,我觉得应该首先了解Rust语言的设计目的是什么?为什么会诞生这门语言?这门语言和其他的语言有什么不同. Rust语言的设计特点 高性能:rust拥有和C++相近的性能表现,所以在嵌入 ...

  2. Oracle用户创建及删除

    偶尔会用到,记录.分享. 1. Oracle用户创建 #创建用户表空间create tablespace $username datafile '/u01/app/oracle/oradata/ufg ...

  3. DNS 解析 prefeath

    本文将详细介绍DNS预解析prefetch的主要内容 概述 DNS(Domain Name System, 域名系统),是域名和IP地址相互映射的一个分布式数据库.DNS 查询就是将域名转换成 IP ...

  4. 某厂面试:如何优雅使用 SPI 机制

    代码不多,文章可能有点长.朋友面试某厂问到的 SPI 机制,联想到自己项目最近写到的 SPI 场景,文章简要描述下 SPI 机制的发展历程 产出背景 因为最近项目中使用分库分表以及数据加密使用到了 S ...

  5. 使用SunnyUI的datagridview常用代码(个人常用)

    1.窗体加载时初始化grid private void LayOut() { dgv.Font = new System.Drawing.Font("微软雅黑", 9F); dgv ...

  6. Java开发学习(四十二)----MyBatisPlus查询语句之条件查询

    一.条件查询的类 MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合. 这个我们在前面都有见过,比如查询所有和分页查询的时候,都有看到过一个Wrapper类, ...

  7. Chrome 103支持使用本地字体,纯前端导出PDF优化

    在前端导出PDF,解决中文乱码一直是一个头疼的问题.要解决这个问题,需要将ttf等字体文件内容注册到页面PDF生成器中.但是之前网页是没有权限直接获取客户机器字体文件,这时就需要从服务器下载字体文件或 ...

  8. VulnHub靶场渗透实战8-DarkHole: 2

    靶场地址:DarkHole: 2 ~ VulnHub DescriptionBack to the Top Difficulty:Hard This works better with VMware ...

  9. 【大数据课程】高途课程实践-Day01:Python编写Map Reduce函数实现各商品销售量展示(类似wordcount)

    〇.概述 1.工具 http://www.dooccn.com/python3/ 在线运行Python代码 2.步骤 (1)⽣成代码测试数据 (2)编写Mapper逻辑 (3)编写Reducer逻辑 ...

  10. bug处理记录:java.util.UnknownFormatConversionException: Conversion = 'Y'

    1. 报错: java.util.UnknownFormatConversionException: Conversion = 'Y' at java.util.Formatter$FormatSpe ...