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

我之前是如何使用bind的?

在这里插入图片描述

一.demo示例

using MsgHander = std::function<void(const TcpConnectionPtr &amp;conn, json &amp;js, Timestamp)>;
unordered_map<int, MsgHander> _msgHandlerMap; // 消息id对应的处理操作

/*****************************************/
// 注册消息回调
    _server.setMessageCallback(std::bind(&amp;ChatServer::onMessage, this, _1, _2, _3));
/*****************************************/

// 注册消息以及对应的Handler回调函数
ChatService::ChatService()
{
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&amp;ChatService::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&amp;ChatService::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&amp;ChatService::oneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&amp;ChatService::addFirend, this, _1, _2, _3)});

    // 群组业务管理相关事件处理回调注册
    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&amp;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映射找到信号函数,然后调用信号函数,信号函数再去调用槽函数,这样就形成了一个闭环

原文地址:https://blog.csdn.net/weixin_52668597/article/details/134679513

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_5879.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注