前言说明

在文中《【学习笔记】开源库之 - 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. Codeforces Round #820 (Div. 3) A-G

    比赛链接 A 题解 知识点:模拟 时间复杂度 \(O(1)\) 空间复杂度 \(O(1)\) 代码 #include <bits/stdc++.h> #define ll long lon ...

  2. letcode刷题记录-day02-回文数

    回文数 题目描述 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标. 你可以假设每种输入只会对应一个答 ...

  3. Java 19 新功能介绍

    点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. Java 19 在2022 年 9 ...

  4. java-代码编写规范

    命名 变量/方法:小驼峰. mBtnHelloWorld 控件 mBtnTest: 按键 mTvTest:文本

  5. miniconda使用

    基本指令 conda create -n xxx python=3.7 // 创建Python3.7的名为xxx虚拟环境 conda env list // 显示所有的虚拟环境 conda activ ...

  6. Archlinux安装Picgo配置Typora

    Typora堪称为markdown界的老大哥,其大名我们多有耳闻,所见即所的就是他的特点.但是在日常使用中,也经常会碰到一些特别的需求,比如:希望图片能够上传到云端. 怎么将markdown即时粘贴的 ...

  7. Arch Linux + KDE 配置&美化(持续更新~)

    Arch Linux + KDE 配置&美化(持续更新~) 这篇文章着重记录archlinux + KDE的一个基本的配置过程.不包括安装过程(使用archInstall.sh).内容大概有以 ...

  8. day33 过滤器filter & 监听器listener & 利用反射创建BaseServlet实现调用自定义业务方法

    Filter过滤器 Fileter可以实现: 1)客户端的请求访问servlet之前拦截这些请求,对用户请求进行预处理 2)对HttpServletResponse进行后处理: 注意 多个Filter ...

  9. Burp Suite

    Burp Suite proxy代理 1.首先在浏览器中设置代理配置 火狐浏览器先点击右上角三个杠--选项--常规--网络设置 2.打开Burp Suite进行抓包 Proxy代理--options中 ...

  10. jQuery基本使用

    目录 一:jQuery查找标签 1.基本选择器 二:分组与嵌套 三:组合选择器 四:jQuery基本筛选器 五:属性选择器 1.属性标签 六:JQuery表单筛选器 1.type属性 2.表单对象属性 ...