实现类似于Qt的Signal和Slot通讯机制

Signal和Slot机制

其实qt提供的对象间通讯机制。

实现细节

思路

实现类似与信号与槽的机制,无非就是做一些薄记工作,qt中通过虚函数和moc来实现。那我们怎么实现?我的思路是signal是一个对象,其他的slot将自己的回调注册进signal对象中,signal完成薄记工作。那么需要实现的细节归于一下几点。

1. 参数的提取与匹配。

2. slot容器的制作。

3. slot的调用。

参数处理

怎么处理参数是一大难题。我们必须推导出各类型,包括类的类型和参数类型,在此我们引入很多特性模板类。

template <typename T>
struct TypeTraits
{
    typedef T type_value;
    typedef T& refType_value;
    typedef const T& constRefType_value;
    typedef T* pointerType_value;
    typedef const T* constPointerType_value;
};

//>>>
template <typename Re>
struct ParameterTraits
{
    static const int size = -1;
};

template <typename Re>
struct ParameterTraits<Re (*)()>
{
    static const int size = 0;
};

template <typename Re, typename T0>
struct ParameterTraits<Re (*)(T0)>
{
    static const int size = 1;
    typedef T0 P0;
};
.......
//>>>>
template <typename Class, typename Re>
struct ParameterTraits<Re (Class::*)()>
{
    static const int size = 0;

    typedef Class classType_value;

};

template <typename Class, typename Re, typename T0>
struct ParameterTraits<Re (Class::*)(T0)>
{
    static const int size = 1;

    typedef T0 P0;

    typedef Class classType_value;

};

template <typename Class, typename Re, typename T0, typename T1>
struct ParameterTraits<Re (Class::*)(T0, T1)>
{
    static const int size = 2;

    typedef T0 P0;
    typedef T1 P1;

    typedef Class classType_value;
};

上述模板类作用在于提取c-style function和c++类成员函数的类类型和参数类型。

//>>>
template<int Size, typename T>
struct ParameterTupleTraitsProxy
{

};

template<typename T>
struct ParameterTupleTraitsProxy<0, T>
{
    typedef std::tuple<> Parameters;
};

template<typename T>
struct ParameterTupleTraitsProxy<1, T>
{
    typedef std::tuple<typename T::P0> Parameters;
};

template<typename T>
struct ParameterTupleTraitsProxy<2, T>
{
    typedef std::tuple<typename T::P0, typename T::P1> Parameters;
};
........
template <typename ParameterTraits>
struct ParameterTupleTraits
{
    typedef typename ParameterTupleTraitsProxy<ParameterTraits::size, ParameterTraits>::Parameters Parameters;
};

上述用来保存signal发送时,参数的保存,传递到slot处的模板类,使用了c++0x的Tuple。

slot

template <typename Paras>
struct SlotBase
{
    virtual ~SlotBase() = 0;
    virtual void dotask(Paras paras) = 0;
};

template <typename Paras>
SlotBase<Paras>::~SlotBase()
{

}

template <typename M>
struct Slot:public SlotBase<typename ParameterTupleTraits<ParameterTraits<M> >::Parameters>
{

    typedef typename TypeTraits<typename ParameterTraits<M>::classType_value>::pointerType_value type_value;
    typedef typename ParameterTupleTraits<ParameterTraits<M> >::Parameters Parameters;
    Slot(type_value sloter, M method):m_object(sloter), m_method(method)
    {

    }

    void dotask(Parameters paras)
    {
        impleDoTask(m_object, m_method, paras);
    }

    type_value m_object;
    M m_method;
};

SlotBase为做容器的基类,dotask函数完成实际的函数调用。Slot保存调用对象和成员函数指针,实现实际的调用,但是参数个数是不同的,这个需要引入对个个参数调用的特例。如下:

template <typename Class, typename Method>
void impleDoTask(Class *c, Method m, std::tuple<> p)
{
    (c->*m)();
}

template <typename Class, typename Method, typename T0>
void impleDoTask(Class *c, Method m,  std::tuple<T0> p)
{
    (c->*m)(std::get<0>(p));
}
template <typename Class, typename Method, typename T0, typename T1>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p));
}

template <typename Class, typename Method, typename T0, typename T1, typename T2, typename T3>
void impleDoTask(Class *c, Method m,  std::tuple<T0, T1, T2, T3> p)
{
    (c->*m)(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p));
}

其对不同个数参数提供具体的模板特化。

Signal

template <typename Method>
struct Signal
{
    ~Signal()
    {
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            delete (*ite);
        }
        sloters.clear();
    }

    template <typename M>
    bool connect(typename Slot<M>::type_value object, M method)
    {
        typename listValue_type::iterator ite = std::find_if(sloters.begin(), sloters.end(), FindHelper<M>(object, method));
        if (ite != sloters.end()) {
            return false;
        }
        sloters.push_back(new Slot<M>(object, method));
        return true;
    }

    template <typename M>
    bool disconnect(typename Slot<M>::type_value object, M method)
    {
        typename listValue_type::iterator ite = std::find_if(sloters.begin(), sloters.end(), FindHelper<M>(object, method));
        if (ite != sloters.end()) {
            delete (*ite);
            sloters.erase(ite);
            return true;
        }
        return false;
    }

    void eemit()
    {
        std::tuple<> para;
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0>
    void eemit(T0 t)
    {
        std::tuple<T0> para = std::make_tuple(t);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1>
    void eemit(T0 t, T1 t1)
    {
        std::tuple<T0, T1> para = std::make_tuple(t, t1);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }

    template <typename T0, typename T1, typename T2>
    void eemit(T0 t, T1 t1, T2 t2)
    {
        std::tuple<T0, T1, T2> para = std::make_tuple(t, t1, t2);
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            (*ite)->dotask(para);
        }
    }
    ......
     //使用Method来实例化list,是为了在connect的时候,防止Method和M不一致,也就是参数不一致。
    typedef std::list<SlotBase<typename ParameterTupleTraits<ParameterTraits<Method> >::Parameters>* > listValue_type;
    listValue_type sloters;

    template <typename T>
    struct FindHelper
    {
        FindHelper(typename Slot<T>::type_value object, T method):m_object(object),m_method(method)
        {

        }

        bool operator()(const typename listValue_type::value_type& val) const
        {
            const Slot<T> *p = static_cast<Slot<T>*>(val);
            return p->m_object == m_object && p->m_method == m_method;
        }

        bool operator()(typename listValue_type::value_type& val)
        {
            const Slot<T> *p = static_cast<Slot<T>*>(val);
            return p->m_object == m_object && p->m_method == m_method;
        }

        typename Slot<T>::type_value m_object;
        T m_method;

    };

};

sloters完成了对各个sloter的保存。connect和disconnect实现了信号连接和断开,eemit实现了信号的发送,其也提供不同个数的参数的模板特化。FindHelper用来防止一个对象重复连接。

例子

#include <iostream>
#include "signal.h"
using namespace std;

struct TTT
{
    int a;
    int b;
    int c;
};

class N
{
public:
    virtual void func_c(int a, int b) = 0;

};

class A : public N
{
public:
    A() {}
    void func_b(int a) {cout << a << "aaaaaa\n";}
    void func_c(int a, int b) {cout << a << "+" << b << "=" << a+b << std::endl;}
    void func_a()
    {
        TTT t = {1, ‘s‘, ‘t‘};
        s.eemit(t, t, t);
    }

    void func_z()
    {
        cout << "zhou xiang  ";
    }

    Signal<void (*)(TTT, TTT, TTT)> s;

};

class B
{
public:
    B(A *a):m_a(a){}
    void func_b(int a) {cout << a << "bbbbbbb\n";}
    void func_slot(TTT t, TTT t2, TTT t3)
    {
        cout << t.a + t2.b + t3.c << "-==-=-=-==-=\n";
    }

    void func_z()
    {
        cout << "love chenchen\n";
    }

    A *m_a;
    void func_e()
    {
        m_a->s.connect(this, &B::func_slot);
    }
};

int main()
{
    A object;
    B object1(&object);

    Signal<void (*)(int, int)> s;

    Signal<void (*)()> ssss;

    ssss.connect(&object, &A::func_z);
    ssss.connect(&object1, &B::func_z);
    ssss.connect(&object1, &B::func_z);

//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    object1.func_e();
//    object1.func_e();

//    object.func_a();

    ssss.eemit();
    ssss.disconnect(&object, &A::func_z);
    ssss.eemit();

    cout << "---------------------------\n";

    s.eemit(4, 456);

    return 0;
}

这是我测试的例子。

这个只是一个雏形,多线程不能直接dotask,应该将调用投递到对象所在线程。

代码地址:https://git.oschina.net/zhouX/signal.git

时间: 2024-10-09 20:32:29

实现类似于Qt的Signal和Slot通讯机制的相关文章

深入了解Qt(三)之元signal和slot

深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt 信号和槽函数这篇文章中已经详细地介绍了信号和槽的使用及注意事项.在这里对其使用方面的知识就不再做介绍,只做一些相应补充. 我们知道信号和槽是用来在对象间通信的一种机制,当一个特定的事件发生时,signal会被emit,slot调用时用来响应响应的signal.signal和slot机制是类型安全的,signal和slot必须互相匹配(实际上一个slot参数可以比对应的signal的参数

Qt 定时器signal/slot阻塞主线程界面

示例代码: <span style="font-size:18px;">class bicycle : public QMainWindow { public slots: void uploadDeviceStatus(); }; bicycle::bicycle(QWidget *parent) : QMainWindow(parent) { QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout(

Signal和slot的声明和连接

信号(SIGNAL)和槽(SLOT)是Qt编程的一个重要部分.这个机制可以在对象之间彼此并不了解的情况下将它们的行为联系起来. Signal和slot的声明(一) 在Qt程序设计中,凡是包含signal和slot的类中都要加上Q_OBJECT的定义,下面的例子给出了如何定义在一个类中定义signal和slot 信号的定义: siganls: void mySignal(); void mySignal( int x ); void mySignal( int x, int y ); 其中sign

Signal和Slot是同步的还是异步的

signal和slot是异步调用还是同步调用,取决于对connect的设定.其实connect还有一个参数(Qt::ConnectionType),是它决定了是同步还是异步.以下是ConnectionType的定义 只不过,平常它有一个默认值Qt::AutoConnection,我们忽略了它.这时有道友问道,为何在AutoConnection模式下,有时是同步,有时是异步,莫非Auto就是人品代名词. 非也,其实Auto是这样规定的, 当sender和receiver在同一线程时,就是同步模式,

一.SuperIO通讯机制

1.1    应用场景 通讯平台的交互对象包括两方面:第一.与硬件产品交互.第二.与软件产品交互.基本这两方面考虑,通讯平台一般会应用在两个场景: 1)通讯平台应用在PC机上 主要应用在自动站的工控机上,通过RS485/RS232.RJ45.4-20mA等方式采集硬件设备的数据信息.同时,通讯平台与服务器端的软件进行交互,负责上传数据信息,以及接收控制命令等. 2)通讯平台应用在服务器端上 终端设备以3G/4G.有线专网.卫星等与通讯平台连接,进行数据交互,终端设备包括:PC机.移动终端(手机)

ActiveMQ之 TCP通讯机制

ActiveMQ支持多种通讯协议TCP/UDP等,我们选取最常用的TCP来分析ActiveMQ的通讯机制.首先我们来明确一个概念:  客户(Client):消息的生产者.消费者对ActiveMQ来说都叫作客户.  消息中介(Message broker):接收消息并进行相关处理后分发给消息的消费者. 为了能清楚的描述出ActiveMQ的核心通讯机制,我们选择3个部分来进行说明,它们分别是建立链接.关闭链接.心跳. 一.Client跟activeMQ的TCP通讯的初始化过程分析如下:  (1) A

【工业串口和网络软件通讯平台(SuperIO)教程】八.SuperIO通讯机制与设备驱动对接的说明

SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1    通讯机制说明 通讯的总体机制采用呼叫应答方式,就是上位机软件主动发送请求数据命令,下位机终端接收到命令后,并校验成功,返回相应的数据. (1)串口通讯机制包括:轮询模式. 串口总线涉及到一对一的通讯.一对多的通讯,为了避免总线上数据的冲突问题以及环境干扰的问题,在保障通讯稳定性的前提下,只采用轮询通讯机制.一个设备发送.接收完成数据后,才进行下一个设备的通讯.但是这种通讯效率比较低,如果要提

一篇文章了解相见恨晚的 Android Binder 进程间通讯机制【转】

本文转载自:https://blog.csdn.net/freekiteyu/article/details/70082302 Android-Binder进程间通讯机制 概述 最近在学习Binder机制,在网上查阅了大量的资料,也看了老罗的Binder系列的博客和Innost的深入理解Binder系列的博客,都是从底层开始讲的,全是C代码,虽然之前学过C和C++,然而各种函数之间花式跳转,看的我都怀疑人生.毫不夸张的讲每看一遍都是新的内容,跟没看过一样.后来又看到了Gityuan的博客看到了一

实现signal和slot机制(2)-跨线程投递

前言 在1中,我们实现了一个基础的signal+slot的模块件调用机制,不过那个direct调用,在这篇中,我们将支持夸线程调用,即在对象的线程上下文中,调用对象的函数. 对象线程绑定 在qt中,有一个很重要的概念,对象线程绑定,即在每个对象实例时候,其保存其线程上下文信息,其中就有一个事件循环,跨线程的信号就是将这个一个task丢到这个事件循环中,在对象绑定的线程中完成slot响应. 实现 对slot调用的打包-Task 我们需要将一个跨线程的slot调用打包成一个task,然后丢到想相应的