纯cpp如何模拟qt的信号与槽

纯cpp如何模拟qt的信号与槽

  • 我之前是如何使用bind的?
  • 一.demo示例
  • 二.简单来讲,c++自带的bind与function函数,如何实现类似信号与槽的机制
    • 1. 简单语法
    • 2. function与bind联动
      • 尝试1
      • 尝试2
      • 真正实现
      • 流程图
  • 自我反思

我之前是如何使用bind的?

在这里插入图片描述

一.demo示例

using MsgHander = std::function<void(const TcpConnectionPtr &conn, json &js, Timestamp)>;
unordered_map<int, MsgHander> _msgHandlerMap; // 消息id对应的处理操作/*****************************************/
// 注册消息回调_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));
/*****************************************/// 注册消息以及对应的Handler回调函数
ChatService::ChatService()
{_msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});_msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});_msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});_msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFirend, this, _1, _2, _3)});// 群组业务管理相关事件处理回调注册_msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});_msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});_msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});
}/*****************************************/// 上报读写时间相关信息的回调函数
void ChatServer::onMessage(const TcpConnectionPtr &conn, Buffer *buffer, Timestamp time)
{string buf = buffer->retrieveAllAsString();// 数据的反序列化json js = json::parse(buf);// 达到的目的:完全解耦网络模块的代码和业务模块的代码// 通过js["msgid"]获取->业务的hander->conn js timeauto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());// 回调消息绑定好的事件处理器,并执行相应的业务处理msgHandler(conn, js, time);
}/*****************************************///  获取消息对应的处理器
MsgHander ChatService::getHandler(int msgid)
{// 记录错误日志,msgid没有对应的事件处理回调auto it = _msgHandlerMap.find(msgid);if (it == _msgHandlerMap.end()){// 返回一个默认的处理器,是一个空操作return [=](const TcpConnectionPtr &conn, json &js, Timestamp){LOG_ERROR<< "Can not find handler:[" << msgid << "]!";};}else{return _msgHandlerMap[msgid];}
}

二.简单来讲,c++自带的bind与function函数,如何实现类似信号与槽的机制

1. 简单语法


#include <functional>
#include <iostream>void print(int arg)
{std::cout<<arg<<std::endl;
}void add(int a, int b)
{std::cout<< a+b <<std::endl;
}int cut(int a , int b)
{return a - b; 
}class A{
public:int number;A(){}A(int num){number = num;}Add(int a,int b);
private:};
A::Add(int a , int b)
{return a + b ;
}int main()
{/******************** 用法一 :* 相当于给一个(返回值是void,参数是int)的函数起一个(别名)* **********************/std::function<void(int)> myPrint = print;myPrint(100);/*****************************************/std::function<void(int,int)> myAdd = add;myAdd(1,2);/****************************************/std::function<int(int,int)> myCut = cut;int ret = myCut(1,2);std::cout<<ret<<std::endl;/****************************************//********* 用法二:* *************/A a1;std::function<int(A&,int,int)> func_ref_add = &A::Add;ret = func_ref_add(a1,3,4);std::cout<<ret<<std::endl;/****************************************************/const A a2(999);std::function<int (A const&)>class_number_call = &A::number;ret = class_number_call(a2);std::cout<<ret<<std::endl;return 0;
}

2. function与bind联动

#include<iostream>
#include<functional>using namespace std;
using namespace std::placeholders;int add(int a,int b)
{return a + b;
}class A{
public:int number;A(){}A(int num){number = num;}Add(int a,int b);
private:};
A::Add(int a , int b)
{return a + b ;
}int main()
{int ret = add(1,1);cout<<ret<<endl;/********* std::placeholders::_1相当于一个占位符,* 如果调用func_add_1只用调用一个参数了,另一个参数是5* ******/function<int(int)>func_add_1 = bind(add,std::placeholders::_1,5);ret = func_add_1(3);cout<<ret<<endl;/********** 还可以直接使用auto* ***************/auto func_add_2 = bind(add,std::placeholders::_1,5);ret = func_add_2(4);cout<<ret<<endl;A classA;//A类的方法,A类的对象,该函数的一些参数设置...auto member_func_bind = std::bind(&A::Add,&classA,std::placeholders::_1,66);ret  = member_func_bind(34);cout<<ret<<endl;return 0;
}

尝试1

#include <iostream>
#include <functional>
#include <map>/*** @brief Signal 类充当信号的角色* */
class Signal {
public:using Slot = std::function<void()>;/**   连接*   signalName   信号            const std::string&*   slot         槽              const std::function<void()>&* 注意:signalName和slot根据根据需求设计出任何函数类型*/void connect(const std::string& signalName, const Slot& slot) {slots_[signalName] = slot;}void emit(const std::string& signalName) {auto it = slots_.find(signalName);if (it != slots_.end()) {it->second();//找到对应的函数,并调用} else {std::cerr << "Signal not connected: " << signalName << std::endl;}}private:std::map<std::string, Slot> slots_;
};/*** @brief Object 类包含了槽函数。* */
class Object {
public:Signal signal;void slot1() {std::cout << "Slot 1 called" << std::endl;}void slot2(int value) {std::cout << "Slot 2 called with value: " << value << std::endl;}
};int main() {Object obj;// Connect slots to signalsobj.signal.connect("signal1", std::bind(&Object::slot1, &obj));obj.signal.connect("signal2", std::bind(&Object::slot2, &obj, 42));// Emit signalsobj.signal.emit("signal1");obj.signal.emit("signal2");return 0;
}
/*
*   Slot 1 called
*   Slot 2 called with value: 42
*/

尝试2

#include <iostream>
#include <string>
#include <map>class Signal {
public:std::string data;std::map<std::string, std::string> parameters;
};class Chatbot {
public:void getResponse(const Signal& signal) {// Access data and parameters from the signalstd::string data = signal.data;std::map<std::string, std::string> parameters = signal.parameters;// Process the signal and provide a response// ...std::cout << "Received signal with data: " << std::endl << data << std::endl;std::cout << "Parameters: " << std::endl;for (const auto& pair : parameters) {std::cout << pair.first << ": " << pair.second << std::endl;}}
};int main() {// Create a signal with data and parametersSignal signal;signal.data = "Hello";signal.parameters["param1"] = "10";signal.parameters["param2"] = "value";// Pass the signal to the chatbotChatbot chatbot;chatbot.getResponse(signal);return 0;
}
/*
Received signal with data: 
Hello
Parameters:
param1: 10
param2: value
*/

真正实现

/**********************上面两个方法只是尝试,现在是真正的实现,如下:************************/
/*** 逻辑:* bind绑定 (string 标识符,信号)* emit 标识符 -> 槽函数(信号)*/
#include <iostream>
#include <functional>
#include <map>/*** @brief Signal 类充当信号的角色* */
class Signal {
public:using SIGNAL = std::function<void()>;/**   连接*   signalName   信号标识符            const std::string&*   slot         信号函数              const std::function<void()>&* 注意:signalName和slot根据根据需求设计出任何函数类型*/void connect(const std::string& signalName, const SIGNAL& signal) {signals_[signalName] = signal;}void emit(const std::string& signalName) {auto it = signals_.find(signalName);if (it != signals_.end()) {it->second();//通过对应信号标识符 调用 信号函数} else {std::cerr << "Signal not connected: " << signalName << std::endl;}}private:std::map<std::string, SIGNAL> signals_;
};/*** @brief Object 类包含了槽函数。* */
class Object {
public:Signal signal;void slot1() {//槽函数1std::cout << "Slot 1 called" << std::endl;}void slot2(int value) {//槽函数1std::cout << "Slot 2 called with value: " << value << std::endl;}void signal1() {//信号函数1this->slot1();}void signal2(int value) {//信号函数2this->slot2(value);}
};int main() {Object obj;// Connect slots to signalsobj.signal.connect("signal1", std::bind(&Object::signal1, &obj));//bind就是对象方法+对象实例obj.signal.connect("signal2", std::bind(&Object::signal2, &obj, 42));// Emit signalsobj.signal.emit("signal1");obj.signal.emit("signal2");return 0;
}
/*
Slot 1 called
Slot 2 called with value: 42
*/

流程图

在这里插入图片描述

自我反思

模拟qt的信号与槽就是在信号的部分多进行一步封装,可以分为三层:信号标识符,信号函数与槽函数,信号标识符可以是int,也可以是string。通过信号标识符与信号函数进行连接,然后通过信号标识符找到信号函数,再使用信号函数调用槽函数。
为什么不直接用信号标识符连接槽函数?因为信号标识符无法携带任何参数,而信号函数可以,我们然后通过信号函数的参数再去调用槽函数,这样就对应了qt的机制emit函数,其实就是:在已经注册了的“信号与槽”中寻找对应的信号标识符,然后再通过map映射找到信号函数,然后调用信号函数,信号函数再去调用槽函数,这样就形成了一个闭环

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/228305.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 SwiftUI 创建一个灵活的选择器

文章目录 前言可选择协议自定义化FlexiblePicker 逻辑FlexiblePicker 视图总结 前言 最近&#xff0c;在我正在开发一个在 Dribbble 上找到的设计的 SwiftUI 实现时&#xff0c;我想到了一个点子&#xff0c;可以通过一些酷炫的筛选器扩展该项目以缩小结果列表。 我决定筛选视…

C++二分查找、离线算法:最近的房间

作者推荐 利用广度优先或模拟解决米诺骨牌 本文涉及的基础知识点 二分查找算法合集 题目 一个酒店里有 n 个房间&#xff0c;这些房间用二维整数数组 rooms 表示&#xff0c;其中 rooms[i] [roomIdi, sizei] 表示有一个房间号为 roomIdi 的房间且它的面积为 sizei 。每一…

【数值计算方法(黄明游)】常微分方程初值问题的数值积分法:欧拉方法(向前Euler)【理论到程序】

文章目录 一、数值积分法1. 一般步骤2. 数值方法 二、欧拉方法&#xff08;Euler Method&#xff09;1. 向前欧拉法&#xff08;前向欧拉法&#xff09;a. 基本理论b. 典例解析c. 算法实现 常微分方程初值问题的数值积分法是一种通过数值方法求解给定初始条件下的常微分方程&am…

【安装指南】MySQL和Navicat下载、安装及使用详细教程

目录 ⛳️1.【MySQL】安装教程 1.1 获取下载包 1.2 MySQL安装 1.2.1 MySQL工具安装 1.2.2 MySQL环境变量 1.2.3 验证MySQL安装成功 ⛳️2.【Navicat-v15】的安装和无限使用 ⛳️3.【测试Navicat连接MySQL】 ⛳️1.【MySQL】安装教程 1.1 获取下载包 前往官网获取压缩包…

UDP实现群聊通信

服务器端 #include <myhead.h> #define UDPIP "192.168.115.92" #define UDPPORT 6666 //存储客户信息的链表结构体 typedef struct Node {char name[20];struct sockaddr_in cin;struct Node *next; }*linklist; //数据结构体 struct data_cli {char type;ch…

分类预测 | Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测

分类预测 | Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测 目录 分类预测 | Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现NGO-KELM北方苍鹰算法优化核极限学习机分类预测&#xff08;完…

只会在终端使用Python运行代码?这些高级用法了解了解

大部分同学在终端使用Python可能只是简单的执行代码&#xff0c;但其实结合一些Python内置模块或第三方库可以实现更高级且便捷的用法&#xff0c;一起看看吧 插播&#xff0c;更多文字总结指南实用工具科技前沿动态第一时间更新在公粽号【啥都会一点的研究生】 代码Benchmar…

【代码】基于卷积神经网络(CNN)-支持向量机(SVM)的分类预测算法

程序名称&#xff1a;基于卷积神经网络&#xff08;CNN&#xff09;-支持向量机&#xff08;SVM&#xff09;的分类预测算法 实现平台&#xff1a;matlab 代码简介&#xff1a;CNN-SVM是一种常用的图像分类方法&#xff0c;结合了卷积神经网络&#xff08;CNN&#xff09;和支…

绝地求生:成长型皮肤异色定价是否有些夸张?

大家好&#xff0c;我闲游盒小盒子&#xff01; 自从26.2更新上架回归的黑市中四款成长型皮肤以后&#xff0c;能看到社区里很多玩家都分享抽中了自己心仪的成长型皮肤。 但是对于异色很少有人去实装&#xff0c;大多数玩家都是选择去分解异色换取五张图纸然后追求升级原皮等级…

【古月居《ros入门21讲》学习笔记】15_ROS中的坐标系管理系统

目录 说明&#xff1a; 1. 机器人中的坐标变换 tf功能包能干什么&#xff1f; tf坐标变换如何实现 2. 小海龟跟随实验 安装 ros-melodic-turtle-tf 实验命令 运行效果 说明&#xff1a; 1. 本系列学习笔记基于B站&#xff1a;古月居《ROS入门21讲》课程&#xff0c;且使…

怎么更新BI报表数据?问我就对了

BI大数据分析工具上有大量的BI报表模板&#xff0c;这些模板都是一个个完整的BI报表&#xff0c;只需将数据源更换&#xff0c;立即就能用来分析我们自己的数据。那&#xff0c;BI报表的数据怎么更新&#xff1f;接下来就来说说这事。 目的&#xff1a;更新BI报表数据 工具&a…

Flask教程入门

1.学习Flask之前&#xff0c;首先需要对URL进行一定的了解。 URL的一些知识&#xff1a; 1.URL只能包含ASCII码里面一些可显示的字符&#xff0c;如A-Z&#xff0c;a-z&#xff0c;0-9&#xff0c;&&#xff0c;#&#xff0c;%&#xff0c;&#xff1f;&#xff0c;/等字符…