C++ 程序设计:信号与槽

1.简介

在C++中,有很多开源的信号与槽库可以使用。以下是一些常见的开源信号与槽库:

  1. Qt:Qt是一个流行的跨平台应用程序框架,提供了一个强大的信号与槽系统。它能够轻松地创建和管理信号与槽的连接,具有良好的可扩展性和灵活性。Qt通过使用线程间事件队列(event queue)来实现线程间的信号与槽通信。

  2. Boost.Signals:Boost.Signals是C++的一个信号与槽库,它提供了一套基于回调机制的信号与槽系统。Boost.Signals使用了一种基于函数指针和函数对象的机制,允许对象之间进行通信和交互。它基于回调机制实现了类似于Qt的信号与槽功能。

  3. 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基类的实现

  1. class SObject:这是一个基类,代表一个对象,拥有信号槽机制。它包含了一些成员变量和成员函数,用于管理信号和槽的连接和断开,以及对象的析构等操作。
    • signals:使用宏定义定义的关键字,表示下面的成员是信号。
    • sigDestroyed:一个 void(void) 类型的信号,代表对象销毁的信号。
  2. SET_CLASS_NAME 宏:用于生成每个类实例的类名,并提供 className() 函数返回类名。
  3. SObject 类的成员函数:
    • connect():连接一个信号与一个槽函数。
      • sender:信号发送者对象。
      • signal:需要连接的信号。
      • receiver:信号接收者对象。
      • SlotFunc:槽函数指针。
    • disconnect():断开一个信号与一个槽函数的连接。
      • sender:信号发送者对象。
      • signal:需要断开连接的信号。
      • receiver:信号接收者对象。
      • SlotFunc:槽函数指针。
    • name():返回对象的名称。
    • parent():返回对象的父对象。
  4. 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 - 线程间的事件队列

待更新

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值