【学习笔记】开源库之 - sigslot (在解决浅拷贝问题的基础上增加信号拦截功能)
前言说明
在文中《【学习笔记】开源库之 - 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()
{