1.简介
在C++中,有很多开源的信号与槽库可以使用。以下是一些常见的开源信号与槽库:
-
Qt:Qt是一个流行的跨平台应用程序框架,提供了一个强大的信号与槽系统。它能够轻松地创建和管理信号与槽的连接,具有良好的可扩展性和灵活性。Qt通过使用线程间事件队列(event queue)来实现线程间的信号与槽通信。
-
Boost.Signals:Boost.Signals是C++的一个信号与槽库,它提供了一套基于回调机制的信号与槽系统。Boost.Signals使用了一种基于函数指针和函数对象的机制,允许对象之间进行通信和交互。它基于回调机制实现了类似于Qt的信号与槽功能。
-
libsigc++:libsigc++是C++的一个类型安全、灵活和高效的信号与槽库。它提供了一个简单易用的接口,可以在C++对象之间进行信号传递和槽连接。当一个信号被触发时,libsigc++会遍历所有与该信号相连接的槽,依次调用这些槽.
2.信号与槽的实现方式1 - 链表方法的实现
2.1槽的实现
/* SSlotAPI 模板声明 */
template<typename F>
class SSlotAPI;
/* SSlotAPI 模板特化 通过特化将模板参数F具体化为函数类型Ret(Args...)*/
template<typename Ret, typename ...Args>
class SSlotAPI<Ret(Args...)>
{
public:
/* 返回成员变量m_type的值,类型为E_SLOT_TYPE。表示槽的类型。*/
E_SLOT_TYPE type() const
{
return m_type;
}
/* 构造函数 用于初始化成员变量m_receiver、m_slot和m_type。*/
explicit SSlotAPI(void *slot, SObject *receiver = nullptr, E_SLOT_TYPE t = C_SLOT_TYPE )
:m_receiver(receiver), m_slot(slot), m_type(t)
{
}
virtual ~SSlotAPI() {}
virtual void operator() (Args &...args) = 0;
/* 比较操作符重载函数 2个SSlotAPI 是否相等 */
bool operator == (const SSlotAPI &other)
{
return other.m_type == m_type
&& other.m_slot == m_slot
&& other.m_receiver == m_receiver ;
}
/* 指向SObject类型的指针,表示槽的接收者。*/
SObject *m_receiver;
private:
/* 赋值操作符重载函数,被删除,禁止对象之间的赋值操作 */
SSlotAPI &operator = (const SSlotAPI &) = delete;
/* 槽的类型 */
E_SLOT_TYPE m_type;
protected:
/* 具体的槽函数 */
void *m_slot;
};
SSlotCpp
是一个模板类,用于定义带有接收者类型和函数类型的槽。它继承自基类 SSlotAPI<Ret(Args...)>
,定义了构造函数和 operator()
函数来实现对槽的初始化和执行操作。通过使用 Receiver
和 Ret(Args...)
,可以定义不同接收者类型和不同函数类型的槽对象。
template<typename Receiver, typename F>
class SSlotCpp;
template<typename Receiver, typename Ret, typename ...Args>
class SSlotCpp<Receiver, Ret(Args...)> : public SSlotAPI<Ret(Args...)>
{
public:
typedef void (Receiver::*SlotFuncType) (Args...);
/* 这是 SSlotCpp 的构造函数
带有两个参数:Receiver* r 和 SlotFuncType slot。
构造函数用于初始化基类 SSlotAPI<Ret(Args...)> 和成员变量 m_class_slot。
通过调用基类的构造函数,将 slot 转换为 void* 类型,
并将 r 和 CPP_SLOT_TYPE 传递给基类的构造函数进行初始化。*/
SSlotCpp(Receiver* r, SlotFuncType slot)
: SSlotAPI<Ret(Args...)>((void*)slot, (SObject*)r, CPP_SLOT_TYPE), m_class_slot(slot)
{
}
virtual ~SSlotCpp() {}
/* 这是 operator() 的函数重载,用于执行槽函数的操作。
它接受参数 Args& ...args,并通过调用指向接收者类的成员函数指针
m_class_slot,将参数传递给槽函数,并执行槽函数。 */
void operator() (Args &...args)
{
(((Receiver *)SSlotAPI<Ret(Args...)>::m_receiver)->*m_class_slot) (args...);
}
private:
SlotFuncType m_class_slot;
};
SSlot
是一个更通用的槽对象类,它可以用于不同类型的槽函数,包括普通函数、成员函数等。而 SSlotCpp
是模板类,特化为特定的函数类型 Ret(Args...)
,用于特定类型的槽函数。此外,SSlot
中的 operator()
是一个虚函数声明,需要在派生类中进行实现,而 SSlotCpp
中的 operator()
是一个函数重载,用于执行特定类型的槽函数。
class SSlot
{
public:
E_SLOT_TYPE type() const
{
return m_type;
}
explicit SSlot(void *slot, SObject *receiver = nullptr, E_SLOT_TYPE t = C_SLOT_TYPE)
:m_receiver(receiver), m_slot(slot), m_type(t)
{
}
virtual ~SSlot() {}
virtual void operator() (const SSlot &);
bool operator == (const SSlot &other)
{
return other.m_type == m_type
&& other.m_slot == m_slot
&& other.m_receiver == m_receiver;
}
SObject *m_receiver;
private:
SSlot &operator= (const SSlot &) = delete;
E_SLOT_TYPE m_type;
protected:
void *m_slot;
};
2.2信号的实现
SSignal
是一个模板类,用于定义信号,并提供发射信号的能力。通过使用 Ret(Args...)
,可以定义不同类型的信号,其中 SSlotAPI<Ret(Args...)>
表示槽对象的类型。emit
函数和 operator()
函数用于发射信号并执行槽函数,遍历存储槽对象的链表 _slotLst
并依次调用槽函数。
/**
* @class SSignal
* @brief SSignal 类用来定义信号,所述信号的函数类型为Ret (*)(Args...)。\n
* 比如:SSignal<void(int)> intSig;//定义一个函数类型为void intSig(int);
* 比如:SSignal<void(int, float)> ifSig;//定义一个函数类型为void ifSig(int, float);
*
*/
template<typename F>
class SSignal;
template<typename Ret, typename... Args>
class SSignal<Ret(Args...)>
{
public:
/* 在这里,定义了一个类型别名 SlotLstType,
它表示一个存储 SSlotAPI<Ret(Args...)> 指针的链表类型 */
typedef list<SSlotAPI<Ret(Args...)>*> SlotLstType;
/* 这是 emit 函数,用于发射信号并执行槽函数。它接受参数 Args...args,
遍历 _slotLst 中的每个槽对象,并将参数传递给槽函数,执行槽函数。 */
void emit(Args ...args)
{
for(auto it:_slotLst)
{
(*it)(args...);
}
}
/* 这是 operator() 的函数重载,与 emit 函数的作用相同。
它接受参数 Args...args,同样遍历 _slotLst 中的每个槽对象,并将参数传递给槽函数,执行槽函数。 */
void operator()(Args ...args)
{
for (auto it:_slotLst)
{
(*it)(args...);
}
}
/* 存储 SSlotAPI<Ret(Args...)> 指针的链表,
通过遍历链表中的每个槽对象,可以对信号相关的槽函数进行操作 */
SlotLstType _slotLst;
};
2.3 Sobject基类的实现
- class SObject:这是一个基类,代表一个对象,拥有信号槽机制。它包含了一些成员变量和成员函数,用于管理信号和槽的连接和断开,以及对象的析构等操作。
- signals:使用宏定义定义的关键字,表示下面的成员是信号。
- sigDestroyed:一个 void(void) 类型的信号,代表对象销毁的信号。
- SET_CLASS_NAME 宏:用于生成每个类实例的类名,并提供 className() 函数返回类名。
- SObject 类的成员函数:
- connect():连接一个信号与一个槽函数。
- sender:信号发送者对象。
- signal:需要连接的信号。
- receiver:信号接收者对象。
- SlotFunc:槽函数指针。
- disconnect():断开一个信号与一个槽函数的连接。
- sender:信号发送者对象。
- signal:需要断开连接的信号。
- receiver:信号接收者对象。
- SlotFunc:槽函数指针。
- name():返回对象的名称。
- parent():返回对象的父对象。
- connect():连接一个信号与一个槽函数。
- SObject 类的私有成员函数和静态成员函数:用于实现信号槽机制的底层操作,例如连接、断开连接、保存和删除信号发送者和接收者等。
#ifndef SOBJECT_H #define SOBJECT_H #include <string> #include <cxxabi.h> #include <list> #include <stdio.h> using namespace std; class SObjectPrivate; class SObject; #define slots enum E_SLOT_TYPE { C_SLOT_TYPE, CPP_SLOT_TYPE, }; template<typename F> class SSlotAPI; template<typename Ret, typename ...Args> class SSlotAPI<Ret(Args...)> { public: E_SLOT_TYPE type() const { return m_type; } explicit SSlotAPI(void *slot, SObject *receiver = nullptr, E_SLOT_TYPE t = C_SLOT_TYPE ) :m_receiver(receiver), m_slot(slot), m_type(t) { } virtual ~SSlotAPI() {} virtual void operator() (Args &...args) = 0; bool operator == (const SSlotAPI &other) { return other.m_type == m_type && other.m_slot == m_slot && other.m_receiver == m_receiver ; } SObject *m_receiver; private: SSlotAPI &operator = (const SSlotAPI &) = delete; E_SLOT_TYPE m_type; protected: void *m_slot; }; template<typename Receiver, typename F> class SSlotCpp; template<typename Receiver, typename Ret, typename ...Args> class SSlotCpp<Receiver, Ret(Args...)> : public SSlotAPI<Ret(Args...)> { public: typedef void (Receiver::*SlotFuncType) (Args...); SSlotCpp(Receiver* r, SlotFuncType slot) : SSlotAPI<Ret(Args...)>((void*)slot, (SObject*)r, CPP_SLOT_TYPE), m_class_slot(slot) { } virtual ~SSlotCpp() {} void operator() (Args &...args) { (((Receiver *)SSlotAPI<Ret(Args...)>::m_receiver)->*m_class_slot) (args...); } private: SlotFuncType m_class_slot; }; /** * @class SSignal * @brief SSignal 类用来定义信号,所述信号的函数类型为Ret (*)(Args...)。\n * 比如:SSignal<void(int)> intSig;//定义一个函数类型为void intSig(int); * 比如:SSignal<void(int, float)> ifSig;//定义一个函数类型为void ifSig(int, float); * */ template<typename F> class SSignal; template<typename Ret, typename... Args> class SSignal<Ret(Args...)> { public: typedef list<SSlotAPI<Ret(Args...)>*> SlotLstType; /** * @brief 发射信号 * @param args 参数列表 * @return void */ void emit(Args ...args) { for(auto it:_slotLst) { (*it)(args...); } } /** * @brief 发射信号 * @param args 参数列表 * @return void */ void operator()(Args ...args) { for (auto it:_slotLst) { (*it)(args...); } } SlotLstType _slotLst; }; class SSlot { public: E_SLOT_TYPE type() const { return m_type; } explicit SSlot(void *slot, SObject *receiver = nullptr, E_SLOT_TYPE t = C_SLOT_TYPE) :m_receiver(receiver), m_slot(slot), m_type(t) { } virtual ~SSlot() {} virtual void operator() (const SSlot &); bool operator == (const SSlot &other) { return other.m_type == m_type && other.m_slot == m_slot && other.m_receiver == m_receiver; } SObject *m_receiver; private: SSlot &operator= (const SSlot &) = delete; E_SLOT_TYPE m_type; protected: void *m_slot; }; #define SIGNAL_TYPE list<SSlot*> #define SIGNAL_POINTER list<SSlot*>* #define SIGNAL_TYPE_ITERATOR list<SSlot*>::iterator #define signals public #define SET_CLASS_NAME(any_type) \ public: \ virtual const char *className() const \ { \ static string s_name; \ char* name = abi::__cxa_demangle(typeid(any_type).name(), NULL, NULL, NULL); \ s_name = name; \ free(name); \ return s_name.c_str(); \ } class SObject { SET_CLASS_NAME(SObject) signals: SSignal<void(void)> sigDestroyed; private: SObjectPrivate *m_priv; public: explicit SObject(SObject *parent = nullptr, const char *name = nullptr); SObject(const SObject &src); SObject &operator = (const SObject &src); virtual ~SObject(); /** * @brief 将信号和槽建立连接。 * Receiver代表接收者的类型 * Args是槽函数/信号的参数列表。 * * @param sender 指向发射者的指针 * @param signal 指向信号的引用。 * @param receiver 指向接收者的指针 * @param slot 指向槽函数的指针 * * @return 0代表成功;非0代表失败 */ template<class Receiver, typename ...Args> static int connect(SObject* sender, SSignal<void(Args...)> &signal, Receiver *receiver, void(Receiver :: *SlotFunc) (Args...)); /** * @brief 将信号和槽断开连接。 * Receiver代表接收者的类型 * Args是槽函数/信号的参数列表。 * * @param sender 指向发射者的指针 * @param signal 指向信号的引用。 * @param receiver 指向接收者的指针 * @param slot 指向槽函数的指针 * * @return 0代表成功;非0代表失败 */ template<class Receiver, typename ...Args> static int disconnect(SObject *sender, SSignal<void(Args...)> &signal, Receiver *receiver, void(Receiver:: *SlotFunc)(Args...)); const char *name() const; SObject *parent() const; private: static int privConnect(SObject *sender, SIGNAL_POINTER signal, SObject *receiver, void *slot); static int privDisconnect(SObject *sender, SIGNAL_POINTER signal, SObject *receiver, void *slot); void saveSenderPair(SObject *sender, SIGNAL_POINTER signal); void deleteSenderPair(SObject *sender, SIGNAL_POINTER signal); void destructAsReceiver(); void destructAsSender(); void saveReceiver(SObject *receiver); void deleteReceiver(SObject *receiver); }; template<class Receiver, typename ...Args> int SObject::connect(SObject *sender, SSignal<void(Args...)>&signal, Receiver *receiver, void(Receiver::*SlotFunc)(Args...)) { SSlotCpp<Receiver, void(Args...)> *vslot = new SSlotCpp<Receiver, void(Args...)>(receiver, SlotFunc); int ret = privConnect(sender, reinterpret_cast<SIGNAL_POINTER>(&(signal._slotLst)), (SObject *)receiver, (void *)vslot); if(0 != ret) { delete vslot; } return ret; } template<class Receiver, typename ...Args> int SObject::disconnect(SObject *sender, SSignal<void(Args...)> &signal, Receiver *receiver, void(Receiver::*SlotFunc)(Args...)) { int ret = privDisconnect(sender, reinterpret_cast<SIGNAL_POINTER>(&(signal._slotLst)), (SObject *)receiver, (void *)SlotFunc); return ret; } #endif // SOBJECT_H
#include "sobject.h" #include <string> #include <algorithm> struct SenderPair { SenderPair(SObject *_sender, SIGNAL_POINTER _signal) : sender(_sender), signal(_signal) { } bool operator == (const SenderPair &r) const { return r.sender == sender && r.signal == signal; } SObject *sender; SIGNAL_POINTER signal; }; class SObjectPrivate { public: SObjectPrivate(SObject *p, const char *nm) : m_parent (p), strName (nm) { spLst.clear(); rLst.clear(); } ~SObjectPrivate() { spLst.clear(); rLst.clear(); } SObject *m_parent; string strName; list<SenderPair> spLst; list<SObject *> rLst; }; SObject::SObject(SObject *p, const char *n) : m_priv(new SObjectPrivate(p, nullptr == n?"":n)) { } SObject::~SObject() { destructAsReceiver(); sigDestroyed(); destructAsSender(); delete m_priv; } void SObject::saveSenderPair(SObject *sender, SIGNAL_POINTER signal) { SenderPair sp(sender, signal); m_priv->spLst.push_back(sp); } void SObject::deleteSenderPair(SObject *sender, SIGNAL_POINTER signal) { m_priv->spLst.remove(SenderPair(sender, signal)); } class Receiver_Is { public: SObject *receiver; bool operator()(SSlot *&obj1) { return obj1->m_receiver == this->receiver; } bool operator()(SObject *receiver) { return this->receiver == receiver; } Receiver_Is(SObject *r) : receiver(r) { } }; class Slot_Is_CppSlot { private: SSlot vslot; public: bool operator()(SSlot *&obj1) { if(obj1->type() != CPP_SLOT_TYPE) { return false; } return vslot == *obj1; } Slot_Is_CppSlot(const SSlot &s) : vslot(s) { } }; int SObject::privConnect(SObject *sender, SIGNAL_POINTER signal, SObject *receiver, void *slot) { if(sender == nullptr || receiver == nullptr || signal == nullptr || slot == nullptr) { printf("can not connect %s::%p to %s::%p\n", sender ? sender->name() : "(null)", signal, receiver ? receiver->name() : "(null)", slot); return -1; } SSlot *vslot = (SSlot *)slot; SIGNAL_TYPE_ITERATOR it = std::find_if(signal->begin(), signal->end(), Slot_Is_CppSlot(*vslot)); if(it != signal->end()) { printf("already connected\n"); return -2; } signal->push_back(vslot); sender->saveReceiver(receiver); receiver->saveSenderPair(sender, signal); return 0; } void SObject::destructAsReceiver() { list<SenderPair>::iterator it = m_priv->spLst.begin(); while(it != m_priv->spLst.end()) { it->signal->remove_if(Receiver_Is(this)); it->sender->m_priv->rLst.remove_if(Receiver_Is(this)); ++it; } } class Sender_Is { public: SObject *sender; bool operator()(SenderPair &obj1) { return obj1.sender == sender; } Sender_Is(SObject *s) : sender(s) { } }; void SObject::destructAsSender() { list<SObject *>::iterator it = m_priv->rLst.begin(); while(it != m_priv->rLst.end()) { SObject *receiver = *it; receiver->m_priv->spLst.remove_if(Sender_Is(this)); ++it; } } void SObject::saveReceiver(SObject *receiver) { m_priv->rLst.push_front(receiver); } void SObject::deleteReceiver(SObject *receiver) { list<SObject*>::iterator it = std::find(m_priv->rLst.begin(), m_priv->rLst.end(), receiver); if(it == m_priv->rLst.end()) { return; } m_priv->rLst.erase(it); } int SObject::privDisconnect(SObject *sender, SIGNAL_POINTER signal, SObject *receiver, void *slot) { if(sender == nullptr || receiver == nullptr || signal == nullptr || slot == nullptr) { printf("can not disconnect sender %s::%p from receiver %s::%p\n", sender ? sender->name() : "(null)", signal, receiver ? receiver->name() : "(null)", slot); return -1; } SIGNAL_TYPE_ITERATOR it = std::find_if(signal->begin(), signal->end(), Slot_Is_CppSlot(SSlot(slot, receiver, CPP_SLOT_TYPE))); if(it == signal->end()) { return -2; } signal->erase(it); sender->deleteReceiver(receiver); receiver->deleteSenderPair(sender, signal); return 0; } const char *SObject::name() const { return m_priv->strName.c_str(); } SObject *SObject::parent() const { return m_priv->m_parent; } SObject &SObject::operator = (const SObject &) { return *this; } SObject::SObject(const SObject &) : m_priv (new SObjectPrivate(nullptr, "")) { } void SSlot::operator() (const SSlot &) { printf("\n"); }
3.信号与槽的实现方式1 的示例使用
#include <string>
#include "sobject.h"
void SysPara::fun_emit(unsigned char *data, int len)
{
printf("signal_videoTaskReadyRead\n");
signal_videoTaskReadyRead.emit(objectType, data, len);
}
/* 系统参数 */
class SysPara : public SObject
{
public:
SysPara();
~SysPara();
void fun_emit(unsigned char *buf, int dataLen);
/**
* 定义信号
*/
SSignal<void(unsigned char *, int)> signal_videoTaskReadyRead;
private:
};
void VideoTask::slot_dataReceived(ObjectType type, unsigned char *data, int len)
{
printf("slot_dataReceived\n");
}
class VideoTask : public SObject
{
public:
VideoTask();
~VideoTask();
protected:
...
private:
...
public slots:
void slot_dataReceived(unsigned char *data, int len);
};
...
syspara = new SysPara();
video = new VideoTask();
SObject::connect(syspara, syspara->signal_videoTaskReadyRead, video, &VideoTask::slot_dataReceived);
/* 调用 syspara.fun_emit() 即可触发槽函数 */
4.信号与槽的实现方式2 - 线程间的事件队列
待更新