C++11实现Qt的信号槽机制

概述

Qt的信号槽机制是Qt的核心机制,按钮点击的响应、线程间通信等都是通过信号槽来实现的,boost里也有信号槽,但和Qt提供的使用接口很不一样,本文主要是用C++11来实现一个简单的信号槽,该信号槽也实现了emit、slots、signals、connect关键字和函数、使用方法和Qt的信号槽基本类似,该信号槽机制用到了C++11的特性有:

  1. 可变参数模板类
  2. 智能指针
  3. 函数相关std::function、std::bind
  4. using关键字
  5. 完美转发std::forward

该信号槽提供类成员函数、类非成员函数的连接、连接时支持std::bind、以及lambda表达式,信号槽机制的核心代码如下:

// Connect.hpp
#ifndef _CONNECT_H
#define _CONNECT_H

#include <vector>
#include <memory>
#include <functional>

#define emit
#define slots
#define signals public
#define connect(sender, signal, slot) ((sender)->signal.bind(slot))

template<typename... Args>
class Slot
{
public:
    using OnFunc = std::function<void(Args&&...)>;

    Slot(const OnFunc& func)
        : m_func(func)
    {
        // Do nothing
    }

    void exec(Args&&... args)
    {
        m_func(std::forward<Args>(args)...);
    }

private:
    OnFunc m_func = nullptr;
};

template<typename... Args>
class Signal
{
public:
    using SlotPtr = std::shared_ptr<Slot<Args&&...>>; 
    using OnFunc = std::function<void(Args&&...)>;

    void bind(const OnFunc& func)
    {
        m_slotVec.push_back(SlotPtr(new Slot<Args&&...>(func)));
    }

    void operator()(Args&&... args)
    {
        for (auto& iter : m_slotVec)
        {
            iter->exec(std::forward<Args>(args)...);
        }
    }

private:
    std::vector<SlotPtr> m_slotVec;
};

#endif


下面是使用C++11信号槽机制的代码:

// main.cpp
/************************************************
 * 该例程讲解用C++11来实现Qt的信号槽机制
 * 使用到的C++11特性有:
 * 1.可变参数模板类
 * 2.智能指针
 * 3.函数相关std::function、std::bind
 * 4.using关键字
 * 5.完美转发std::forward
************************************************/
#include "Connect.hpp"
#include <iostream>
#include <string>

class A
{
public:
    void start()
    {
        emit m_s1();
        emit m_s2("Hello C++11");
        emit m_s3(100, "Hello C++11");
    }

signals:
    Signal<> m_s1;  // 不带参数的信号
    Signal<std::string> m_s2;
    Signal<int, std::string> m_s3;
};

class B
{
public slots:
    void func1()
    {
        std::cout << "func1" << std::endl;
    }

    void func2(const std::string& str)
    {
        std::cout << str << std::endl;
    }

    void func3(int n, const std::string& str)
    {
        std::cout << n << " " << str << std::endl;
    }
};

void func(const std::string& str)
{
    std::cout << "func " << str << std::endl;
}

int main()
{
    A a;
    B b;

    // 信号与槽绑定
    connect(&a, m_s1, std::bind(&B::func1, &b));
    connect(&a, m_s2, std::bind(&B::func2, &b, std::placeholders::_1));
    connect(&a, m_s3, std::bind(&B::func3, &b, std::placeholders::_1, std::placeholders::_2));
    connect(&a, m_s2, std::bind(func, std::placeholders::_1));
    connect(&a, m_s2, [](const std::string& str)
            {
                std::cout << "lambda str: " << str << std::endl;
            });

    a.start();

    return 0;
}


TODO

该例子只是实现了简单的信号槽机制,还有很多功能都没有实现

  1. 不支持断开信号与槽的连接disconnect
  2. 不支持AutoConnection、DirectConnection、QueuedConnection、UniqueConnection
  3. 不支持无锁连接
  4. etc...

该例子的github地址:https://github.com/chxuan/samples/tree/master/Connect

posted @ 2016-06-04 21:26  chxuan  阅读(8264)  评论(2编辑  收藏  举报