当前位置: 首页 > article >正文

纯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映射找到信号函数,然后调用信号函数,信号函数再去调用槽函数,这样就形成了一个闭环


http://www.kler.cn/a/155826.html

相关文章:

  • WebGIS三维地图框架--Cesium
  • python: postgreSQL using psycopg2 or psycopg
  • MySql结合element-plus pagination的分页查询
  • 【C++】new操作符的使用说明
  • 【C++】C++11特性(上)
  • 深入理解接口测试:实用指南与最佳实践5.0(一)
  • 计算UDP报文CRC校验的总结
  • vue2+element-ui npm run build打包后,在服务器打开报错
  • vue 使用decimal.js 解决小数相加合计精确度丢失问题
  • 强化学习------时序差分(Temporal-Difference Learning)
  • 【开源】基于Vue.js的超市账单管理系统的设计和实现
  • Mybatis使用注解实现复杂动态SQL
  • 【CVE-2023-49103】ownCloud graphapi信息泄露漏洞(2023年11月发布)
  • 栈和队列的OJ题--13.用队列实现栈
  • java_基础——ArrayList
  • Spring一些基础问题整理
  • 谱方法学习笔记-下(超详细)
  • 基于Java SSM框架+Vue实现旅游资源网站项目【项目源码+论文说明】计算机毕业设计
  • 【云原生Prometheus篇】Prometheus PromQL语句详解 1.0
  • 使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
  • Redis部署脚本(完成-第一版)
  • shell命令编写
  • 正则表达式从放弃到入门(2):grep命令详解
  • 机器学习---pySpark代码开发
  • 实体类转SQL工具类
  • 在南昌找工作应该上什么网