概述
Qt的信號槽機制是Qt的核心機制,按鈕點擊的響應、線程間通信等都是通過信號槽來實現的,boost里也有信號槽,但和Qt提供的使用接口很不一樣,本文主要是用C++11來實現一個簡單的信號槽,該信號槽也實現了emit、slots、signals、connect關鍵字和函數、使用方法和Qt的信號槽基本類似,該信號槽機制用到了C++11的特性有:
- 可變參數模板類
- 智能指針
- 函數相關std::function、std::bind
- using關鍵字
- 完美轉發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
該例子只是實現了簡單的信號槽機制,還有很多功能都沒有實現
- 不支持斷開信號與槽的連接disconnect
- 不支持AutoConnection、DirectConnection、QueuedConnection、UniqueConnection
- 不支持無鎖連接
- etc...
該例子的github地址:https://github.com/chxuan/samples/tree/master/Connect