下部分代码来自于C++实现自己的信号槽_小青年儿-CSDN博客,我自己添加了传递参数的部分
#ifndef MY_OBJECT
#define MY_OBJECT
#include <map>
#define my_slots
#define my_signals protected
#define my_emit
class MyObject;
struct MetaObject
{
//signal字符串
const char* sig_names;
//slot 字符串
const char* slts_names;
static void active(MyObject* sender,int idx,void** argv);
};
struct Connection
{
//接收者
MyObject* receiver;
//保存的并不是 槽函数,而是slot在receiver里的序号,到时候根据此序号调用对应槽函数
int method;
};
//key 代表signal在sender里的序号,value就是 slot所需的信息
typedef std::multimap<int,Connection> ConnectionMap;
typedef std::multimap<int,Connection>::iterator ConnectionMapIt;
class MyObject
{
//这一部分就是完整的signal和slot信息
static MetaObject meta;
void metacall(int idx,void** argv);
public:
MyObject();
virtual ~MyObject();
static void Connect(MyObject* sender,const char* signal,
MyObject* receiver,const char* slot);
void testSignal();
my_signals:
void mysig(int,double);
public my_slots:
void myslot(int ,double);
friend struct MetaObject;
private:
//其实signal,slot链接map在发送object里的,毕竟调用slot
//是从发送方开始的
ConnectionMap connections;
};
#endif
#include "StdAfx.h"
#include "MyObject.h"
#include <string.h>
#include <stdio.h>
MyObject::MyObject(void)
{
}
MyObject::~MyObject(void)
{
}
//去signal和slot字符串中查找signal和slot的序号
static int find_string(const char* str,const char* substr)
{
if (strlen(substr)>strlen(str))
return -1;
int idx = 0;
int len = strlen(substr);
bool start = true;
const char* pos = str;
while(*pos)
{
if (start&&!strncmp(pos,substr,len)&&pos[len]=='\n')
return idx;
start = false;
if(*pos=='\n')
{
idx++;
start = true;
}
pos++;
}
return -1;
}
//把sender的signal与receiver的slot保存到sender的map里
void MyObject::Connect(MyObject* sender,const char* signal,
MyObject* receiver,const char* slot)
{
int sig_idx = find_string(sender->meta.sig_names,signal);
int slt_idx = find_string(receiver->meta.slts_names,slot);
if(sig_idx==-1||slt_idx==-1)
printf("signal or slot not found");
else
{
Connection c = {receiver,slt_idx};
//保存在 sender的 map里
sender->connections.insert(std::pair<int,Connection>(sig_idx,c));
}
}
void MyObject::myslot(int value1,double value2){
printf("我是槽,不是草 传的参数:int:%d double %lf\n",value1,value2);
};
//这个函数大概就是sender与receiver的中间件,利用保存在MetaObject
//里的signal字符串与slot字符串,去调用那个和signal相应的receiver的slot
void MetaObject::active(MyObject* sender,int idx,void** argv)
{
ConnectionMapIt it;
std::pair<ConnectionMapIt,ConnectionMapIt> ret;
ret = sender->connections.equal_range(idx);
for (it = ret.first;it!=ret.second;++it)//依次调用某信号对应的N个槽
{
Connection c = (*it).second;
c.receiver->metacall(c.method,argv);
}
}
void MyObject::testSignal()
{
my_emit mysig(11,22.2);
}
//每一个\n就分隔出来一个 signal信号 的字符串,这个字符串来自signal信号函数名字,
//在Qt里这一部分功能是由moc实现的
static const char sig_names[] ="mysig\n";
//slot字符串产生原理如上
static const char slt_names[] ="myslot\n";
MetaObject MyObject::meta = {sig_names,slt_names};
//Qt里面定义的signal函数只有定义,然后靠moc生成函数体
void MyObject::mysig(int value1,double value2)
{
//参数是靠void* 数组传递过去的
void* argv[] = {nullptr,reinterpret_cast<void*>(&value1),reinterpret_cast<void*>(&value2)};
//在signal函数里调用active,最终会调用到相应的slot函数
MetaObject::active(this,0,argv);
}
//按照slot号去调用相应的函数
void MyObject::metacall(int idx,void** argv)
{
switch(idx)
{
case 0:
//这里把void*参数强转成slot的参数,moc在编译阶段就已经知道slot参数类型了
myslot(*reinterpret_cast<int*>(argv[1]),*reinterpret_cast<double*>(argv[2]));
break;
default:
break;
}
}
//main.cpp
#include "stdafx.h"
#include "MyObject.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
MyObject obj1,obj2;
MyObject::Connect(&obj1,"mysig",&obj2,"myslot");
obj1.testSignal();
system("pause");
return 0;
}