纯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 time
    auto 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 signals
    obj.signal.connect("signal1", std::bind(&Object::slot1, &obj));
    obj.signal.connect("signal2", std::bind(&Object::slot2, &obj, 42));

    // Emit signals
    obj.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 signal
        std::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 parameters
    Signal signal;
    signal.data = "Hello";
    signal.parameters["param1"] = "10";
    signal.parameters["param2"] = "value";

    // Pass the signal to the chatbot
    
    Chatbot 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() {//槽函数1
        std::cout << "Slot 1 called" << std::endl;
    }

    void slot2(int value) {//槽函数1
        std::cout << "Slot 2 called with value: " << value << std::endl;
    }

    void signal1() {//信号函数1
        this->slot1();
    }

    void signal2(int value) {//信号函数2
        this->slot2(value);
    }
};

int main() {
    Object obj;

    // Connect slots to signals
    obj.signal.connect("signal1", std::bind(&Object::signal1, &obj));//bind就是对象方法+对象实例
    obj.signal.connect("signal2", std::bind(&Object::signal2, &obj, 42));

    // Emit signals
    obj.signal.emit("signal1");
    obj.signal.emit("signal2");

    return 0;
}
/*
Slot 1 called
Slot 2 called with value: 42
*/

流程图

在这里插入图片描述

自我反思

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣2.两数相加

题目描述 把题读懂后&#xff0c;这道题存在两个需要解决的问题&#xff1a;1.进位问题&#xff1b;2.两个链表长度不一 代码 class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//创建新链表的伪指针&#xff0c;指向链表的头结点ListNode prev n…

Go字符串类型

一、字符串 1、字符串 Go 语言里的字符串的内部实现使用 UTF-8 编码字符串带的值为双引号&#xff08;"&#xff09;中的内容&#xff0c;可以在 Go 语言的源码中直接添加非ASCII 码字符 s1 : "hello" s2 : "您好" 2、字符串转义符 Go 语言的字符…

android开发:安卓13Wifi和热点查看与设置功能

近日对安卓热点功能做了一些技术验证&#xff0c;目的是想利用手机开热点给设备做初始化&#xff0c;用的是安卓13&#xff0c;简言之&#xff1a; 热点设置功能不可用&#xff0c;不可设置SSID和密码&#xff0c;不可程序控制开启关闭&#xff0c;网上的代码统统都过时了Loca…

图书管理系统源码,图书管理系统开发,图书借阅系统源码整体功能演示

用户登录 基础资料 操作员管理 超期罚金设置 读者分类 读者管理 图书分类 图书管理 图书借还管理 图书借取 图书还去 图书借还查询 读者借书排行 用户登录 运行View目录下Login文件夹下的Index.csthml出现登录界面&#xff0c;输入用户名密码分别是admin密码admin12…

网络安全小白自学

一、网络安全应该怎么学&#xff1f; 1.计算机基础需要过关 这一步跟网安关系暂时不大&#xff0c;是进入it行业每个人都必须掌握的基础能力。 计算机网络计算机操作系统算法与数据架构数据库 Tips:不用非要钻研至非常精通&#xff0c;可以与学习其他课程同步进行。 2.渗透技…

【数据结构】排序效率最优解之一:二叉树-堆

Hello everybody!今天打算给大家介绍一个功能比较强大的数据结构的基础&#xff0c;它不仅具有很高的应用价值而且排序效率很高。冒泡排序都知道叭&#xff0c;它的时间复杂度为O(n^2)&#xff0c;而堆排序的时间复杂度为O(n*logn)。堆排序直接碾压冒泡排序。在c语言阶段&#…

线性系统理论 -- 降阶观测器的设计

定理&#xff1a; 若系统能观测&#xff0c;且rankCm&#xff0c;则系统的状态观测器的最小维数是(n-m)。 线性定常时不变系统方程如下&#xff08;以三阶(n3)单入单出系统为例&#xff0c;有mrankC1&#xff09;&#xff1a; 取变换阵P&#xff0c;有&#xff1a; 对上述系统…

易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC]

文章目录 易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 易宝OA系统ExecuteSqlForSingle接口SQL注入漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章…

前端量子纠缠 效果炸裂 multipleWindow3dScene

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; &#x1f383; 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲 ✈️已经旅游的地点 | 新疆-乌鲁木齐、新疆-吐鲁番、广东-广州…

免交互语法expect

目录 前瞻 相关命令 范例一&#xff1a;免密登录另外一台主机并创建用户 范例二&#xff1a;免密登录另外三台主机并创建用户 前瞻 expect是建立在tcl&#xff08;tool command language&#xff09;语言基础上的一个工具&#xff0c;常被用于进行自动化控制和测试&#xf…

HbuilderX 项目打包文件过大问题优化

文章目录 HbuilderX 项目打包文件过大问题优化主要操作收效甚微&#xff0c;但又有那么点用的方法使用 gulp 压缩&#xff08;最后一步&#xff09;使用与配置 网上找的 gulp 优化压缩配置还未尝试可能有用的方法 尝试过程中看到的一些优质文章 HbuilderX 项目打包文件过大问题…

ubuntu配置免密登录vscode

1、配置免密登录 &#xff08;1&#xff09;在windows系统cmd下运行命令 ssh-keygen 一路回车&#xff0c;将会在C:\Users\用户名\.ssh目录下生成两个文件&#xff1a;id_rsa和id_rsa.pub。如下图所示。 &#xff08;2&#xff09;进入.ssh目录。如果想使用root用户&#xff0…

ArrayList与顺序表的简单理解

前言----list 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。Collection也是一个接口&#xff0c;该接口中规范了后序容器中常用的一些方法&#xff0c;具体如下所示&#xff1a; Iterable也是一个接口&#xff0c;表示实现该接口的类是可以逐个元素进…

鸿蒙4.0开发笔记之ArkTS语法基础@Entry@Component自定义组件的使用(九)

文章目录 一、自定义组件概述1、什么是自定义组件2、自定义组件的优点 二、创建自定义组件1、自定义组件的结构2、自定义组件要点3、成员变量的创建4、参数传递规则 三、练习案例 一、自定义组件概述 1、什么是自定义组件 在ArkUI中&#xff0c;UI显示的内容均为组件&#xf…

443. 压缩字符串

这篇文章会收录到 : 算法通关村第十二关-黄金挑战字符串冲刺题-CSDN博客 压缩字符串 描述 : 给你一个字符数组 chars &#xff0c;请使用下述算法压缩&#xff1a; 从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 &#xff1a; 如果这一组长度为 1 &#xff0c;…

c++ opencv使用drawKeypoints、line实现特征点的连线显示

前言 图像经过算子处理后得到若干特征点&#xff0c;使用opencv进行渲染显示出这些特征点并且连线&#xff0c;更直观的对比处理前后的一些差异性 demo核心代码 //画出特征点并连线 void drawFilterLinePoints(cv::Mat& srcMat, cv::Point2f pointStart, cv::Point2f po…

基于FactoryBean、实例工厂、静态工厂创建Spring中的复杂对象

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

LD_PRELOAD劫持

LD_PRELOAD劫持 <1> LD_PRELOAD简介 LD_PRELOAD 是linux下的一个环境变量。用于动态链接库的加载&#xff0c;在动态链接库的过程中他的优先级是最高的。类似于 .user.ini 中的 auto_prepend_file&#xff0c;那么我们就可以在自己定义的动态链接库中装入恶意函数。 也…

P9240 [蓝桥杯 2023 省 B] 冶炼金属(比值问题)

数学分析&#xff1a; 1. max(最大比值) A/B 余数p&#xff08;p<B&#xff09; > Amax*Bp 反证&#xff1a;若max不为最大,则设maxn为最大比值 (maxn)*Bmax*Bn*Bp1 > A (n*Bp1 > p ,矛盾) 故max为最大比值 2.min(最小比值…

随时随地,打开浏览器即可体验的在线PS编辑器

即时设计 即时设计是国产的专业级 UI 设计工具&#xff0c;不限平台不限系统&#xff0c;在浏览器打开即用&#xff0c;能够具备 Photoshop 的设计功能&#xff0c;钢笔、矢量编辑、矩形工具、布尔运算等设计工具一应俱全&#xff0c;是能够在线使用的 Photoshop 免费永久工具…