纯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.rhkb.cn/news/208834.html

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

相关文章

【C++】C++11

一、C11 简介 C11 - cppreference.com 在 2003 年 C 标准委员会曾经提交了一份技术勘误表&#xff08;简称TC1&#xff09;&#xff0c;使得 C03 这个名字已经取代了 C98 称为 C11 之前的最新 C 标准名称。不过由于 C03&#xff08;TC1&#xff09;主要是对 C98 标准中的漏洞进…

QT中的 容器(container)-大全

一、介绍 Qt库提供了一套通用的基于模板的容器类&#xff0c;可以用这些类存储指定类型的项。比如&#xff0c;你需要一个大小可变的QString的数组&#xff0c;则使用QVector<QString>。 这些容器类比STL&#xff08;C标准模板库&#xff09;容器设计得更轻量、更安全并…

Xilinx Zynq-7000系列FPGA多路视频处理:图像缩放+视频拼接显示,提供工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐FPGA图像处理方案FPGA图像缩放方案FPGA视频拼接叠加融合方案推荐 3、设计思路详解HLS 图像缩放介绍Video Mixer介绍 4、vivado工程介绍PL 端 FPGA 逻辑设计PS 端 SDK 软件设计 5、工程移植说明vivado版本不一致处理FPGA型号不一致处理其他…

基于springboot 学生学情预警系统-计算机毕设 附源码57567

springboot 学生学情预警系统 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运…

【Java Spring】Spring MVC基础

文章目录 1、Spring MVC 简介2、Spring MVC 功能1.1 Spring MVC 连接功能2.2 Spring MVC 获取参数2.2.1 获取变量2.2.2 获取对象2.2.3 RequestParam重命名后端参数2.2.4 RequestBody 接收Json对象2.2.5 PathVariable从URL中获取参数 1、Spring MVC 简介 Spring Web MVC是构建于…

智慧城市包括哪些内容?有哪些智慧城市物联网方案?

数字城市、智慧城市的发展&#xff0c;离不开对公共基础设施的数字化、智慧化改造升级。通过融合边缘计算、5G、物联网、数字孪生、人工智能等新一代信息技术&#xff0c;助力传统公共基础设施提升增强全流程数据能力、计算能力、服务能力&#xff0c;从而不断丰富公共基础设施…

深度学习记录--logistic回归函数的计算图

计算图用于logistic回归函数 先回顾一下单一样本的logistic回归损失函数的公式&#xff0c;公式如下&#xff1a; 将logistic函数用计算图表示出来(以两个基础量为例)&#xff0c;计算图如下&#xff1a; 前向传播已经完成&#xff0c;接下来完成后向传播 运用链式法则依次求…

[PyTorch][chapter 2][李宏毅深度学习-Regression]

前言&#xff1a; Regression 模型主要用于股票预测,自动驾驶,推荐系统等领域. 这个模型的输出是一个scalar。这里主要以下一个线性模型为基础 它是神经网络的基础模块&#xff0c; 目录&#xff1a; 总体流程 常见问题 Numpy 例子 PyTorch 例子 一 总体流程 1 : 建…

后端Long型数据传到前端js后精度丢失的问题

假设一个场景&#xff0c;MybatisPlus的雪花算法生成long类型主键ID&#xff0c;存入数据库&#xff0c;前端获取到数据后&#xff0c;要执行一个更新操作&#xff08;updateById&#xff09;&#xff0c;但这时会出现无法成功更新的情况&#xff01;这是因为前端在长度大于17位…

【SpringMVC】Spring Web MVC入门(一)

文章目录 前言什么是Spring Web MVC&#xff1f;什么是MVC什么是Spring MVC&#xff1f; Spring Boot 和 Spring MVC 的区别什么是Spring Boot&#xff1f;关系和区别 Spring MVC 学习注解介绍1. SpringBootApplication2. RestController3. RequestMapping3.1 RequestMapping 使…

视频生成的发展史及其原理解析:从Gen2、Emu Video到PixelDance、SVD、Pika 1.0

前言 考虑到文生视频开始爆发&#xff0c;比如11月份就是文生视频最火爆的一个月 11月3日&#xff0c;Runway的Gen-2发布里程碑式更新&#xff0c;支持4K超逼真的清晰度作品(runway是Stable Diffusion最早版本的开发商&#xff0c;Stability AI则开发的SD后续版本)11月16日&a…

Wireshark抓包分析RTMP协议时,出现Unknown问题

进行rtmp推流时&#xff0c;使用wireshark抓包&#xff0c;发现部分包显示Unknown 解决方法&#xff1a; 编辑 -> 首选项 -> Protocols -> RTMPT&#xff0c;这里Maximum packet size默认是32768 将该值调大&#xff0c;比如调成1048576&#xff0c;即可解决该问题。…

【内网安全】搭建网络拓扑,CS内网横向移动实验

文章目录 搭建网络拓扑 ☁环境CS搭建,木马生成上传一句话&#xff0c;获取WebShellCS上线reGeorg搭建代理&#xff0c;访问内网域控IIS提权信息收集横向移动 实验拓扑结构如下&#xff1a; 搭建网络拓扑 ☁ 环境 **攻击者win10地址&#xff1a;**192.168.8.3 dmz win7地址&…

第 374 场 LeetCode 周赛题解

A 找出峰值 枚举 class Solution { public:vector<int> findPeaks(vector<int> &mountain) {int n mountain.size();vector<int> res;for (int i 1; i < n - 1; i)if (mountain[i] > mountain[i - 1] && mountain[i] > mountain[i 1…

人工智能 - 人脸识别:发展历史、技术全解与实战

目录 一、人脸识别技术的发展历程早期探索&#xff1a;20世纪60至80年代技术价值点&#xff1a; 自动化与算法化&#xff1a;20世纪90年代技术价值点&#xff1a; 深度学习的革命&#xff1a;21世纪初至今技术价值点&#xff1a; 二、几何特征方法详解与实战几何特征方法的原理…

阅读笔记|A Survey of Large Language Models

阅读笔记 模型选择&#xff1a;是否一定要选择参数量巨大的模型&#xff1f;如果需要更好的泛化能力&#xff0c;用于处理非单一的任务&#xff0c;例如对话&#xff0c;则可用选更大的模型&#xff1b;而对于单一明确的任务&#xff0c;则不一定越大越好&#xff0c;参数小一…

C++11--右值引用

目录 基本概念 左值和右值 左值引用和右值引用 右值引用使用场景和意义 左值引用使用场景 左值引用的短板 右值引用和移动语义 右值引用引用左值 右值引用的其他使用场景 完美转发 万能引用 完美转发保持值得属性 完美转发使用得场景 基本概念 左值和右值 什么…

前端面试灵魂提问(1)

1.自我介绍 2.在实习中&#xff0c;你负责那一模块 3.any与unknow的异同 相同点&#xff1a;any和unkonwn 可以接受任何值 不同点&#xff1a;any会丢掉类型限制&#xff0c;可以用any 类型的变量随意做任何事情。unknown 变量会强制执行类型检查&#xff0c;所以在使用一个…

Linux线程安全,死锁,生产消费模型,线程池

目录 1.可重入和线程安全 2.死锁 死锁四个必要条件&#xff1a; 避免死锁 3. Linux线程同步 线程同步 生产消费模型的概念理解&#xff08;321原则&#xff09; 生产消费模型都有哪些好处。 串行、并发、并行 条件变量 4.信号量 5.线程池 人的一生为什么要坚持&#xff1…

【ArcGIS Pro微课1000例】0043:深度学习--框架库安装

ArcGIS Pro 中的所有深度学习地理处理工具都要求安装支持的深度学习框架库。 文章目录 深度学习框架库下载深度学习框架库安装注意事项深度学习框架库下载 由于是python开发,可以采用安装包与Pip两种方式安装,本文讲解采用安装包安装。 点击深度学习框架库下载,打开网站,…