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

时间:2023-03-09 08:05:36
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