2021-08-05

C++模拟实现QT信号槽

为了看着更像QT的信号槽,我们也定义一个QObject的父类。

class QObject
{
public:
	QObject* self()
	{
		return this;
	}
	//获取信号的发送者
	std::function<QObject* (void)>  _sender;
};

信号的接收者

class Slot :public QObject
{
public:
	Slot() {}
public:
    //槽函数1
    //如需多个参数 可以将void* signal改为void **signal
	void slot1(void* signal)
	{   
	    //无法使用void*将signal强转为对应类型 使用*(reinterpret_cast<xxx*>(signal));接收
		double res = *(reinterpret_cast<double*>(signal));
		//signal可以强转为对应类型 直接强转即可
		//int res = (int)signal;
		if (_sender) {
		//获得了信号发送者的实例 
		/*
		do something
        */	
		}
		std::cout << "信号发送的参数 :" << res << " 接收者地址 :" << this << std::endl;
	}
	//槽函数2
	void slot2(void* signal) {
	 //无法使用void*将signal强转为对应类型 使用*(reinterpret_cast<xxx*>(signal));接收
		double res = *(reinterpret_cast<double*>(signal));
		//signal可以强转为对应类型 直接强转即可
		//int res = (int)signal;
		if (_sender) {
		//获得了信号发送者的实例 
		/*
		do something
        */	
		}
		std::cout << "信号发送的参数 :" << res << " 接收者地址 :" << this << std::endl;
	}
};

信号的发送者

class Signal :public QObject
{
public:
//如需多个参数 可以将void* signal改为void **signal
	void emitsignal(void *signal)
	{
	 //向信号的接收者传递实例
		_recver->_sender = std::bind(&Signal::self, this);
		string tres = __FUNCTION__;
		tres += "(";
		tres += typeid(signal).name();
		tres += ")";
		//由于没有实现QT的反射机制,暂且使用函数名及参数类型来获取对应的函数信息 tres值为Signal::emitsignal(void *signal)
		for (map<string, vector<function<void(Slot*, void*)>>>::iterator iter = callbacks.begin(); iter != callbacks.end(); iter++) {
		//遍历该函数信息对应的所有槽函数进行触发
			if (strcmp(iter->first.c_str(), tres.c_str()) == 0) {
				for (int i = 0; i < iter->second.size(); i++) {
					iter->second[i](_recver, signal);
				}
			}
		}

	}
	//存储信号和对应的槽函数集合
	map<string, vector<function<void(Slot*, void*)>>> callbacks;
	Slot* _recver;
};

连接函数Connect

//由于未能实现反射机制,先使用字符串ID来标识具体信号
//如需多个参数 可以将void* signal改为void **signal
void connect(Signal* sender, string signal, Slot* recver, function<void(Slot*, void*)> slot)
{
    //接收槽实例
	sender->_recver = recver;
	//向对应的信号中添加待触发的槽函数
	sender->callbacks[signal].push_back(slot);
}

所有代码及测试用例

#include <functional>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
//将参数x强制转换为为void*
#define SIG(x) const_cast<void*>(reinterpret_cast<void*>(x))
class QObject
{
public:
	QObject* self()
	{
		return this;
	}
	std::function<QObject* (void)>  _sender;
};
class Slot :public QObject
{
public:
	Slot() {}
public:

	void slot1(void* signal)
	{
       double res = *(reinterpret_cast<double*>(signal));
		//int res = (int)signal;
		if (_sender) {
		}
		std::cout << "信号发送的参数 :" << res << " 接收者地址 :" << this << std::endl;
	}
	void slot2(void* signal) {
		double res = *(reinterpret_cast<double*>(signal));
		if (_sender) {
		
		}
		std::cout << "信号发送的参数 :" << res << " 接收者地址 :" << this << std::endl;
	}
};

class Signal :public QObject
{
public:
	void emitsignal(void* signal)
	{
		_recver->_sender = std::bind(&Signal::self, this);
		string tres = __FUNCTION__;
		tres += "(";
		tres += typeid(signal).name();
		tres += ")";
		for (map<string, vector<function<void(Slot*, void*)>>>::iterator iter = callbacks.begin(); iter != callbacks.end(); iter++) {
			if (strcmp(iter->first.c_str(), tres.c_str()) == 0) {
				for (int i = 0; i < iter->second.size(); i++) {
					iter->second[i](_recver, signal);
				}
			}
		}

	}
	map<string, vector<function<void(Slot*, void*)>>> callbacks;
	Slot* _recver;
};
//连接函数
void connect(Signal* sender, string signal, Slot* recver, function<void(Slot*, void*)> slot)
{
	sender->_recver = recver;
	sender->callbacks[signal].push_back(slot);
}


int main()
{

	Signal signalobject;
	Slot   slotobject;
	connect(&signalobject, "Signal::emitsignal(void *)", &slotobject, &Slot::slot1);
	connect(&signalobject, "Signal::emitsignal(void *)", &slotobject, &Slot::slot2);
	//信号类型任意 只需与槽函数的参数对应即可
	double param = 10.25;
	signalobject.emitsignal(SIG(&param));
	//int param =10;
	//signalobject.emitsignal(SIG(10));
	return 0;
}

结果

2021-08-05

上一篇:WPF ICommand 接口


下一篇:LeetCode - Easy - 872. Leaf-Similar Trees