C++变参Callback模板封装和CallbackResponder

更多精彩内容,欢迎关注作者微信公众号:码工笔记

最近在使用C++实现一个功能的时候,遇到了一个需要封装变参callback类型的场景,特别记录一下。

基本思路就是有一个void(*)()的指针用来做方法地址的存储,然后使用一个模板定义参数和返回值类型,并在真正调用时,将存储的方法地址cast成模板定义的方法类型,如下:

CPP 复制代码
class CallbackBase {
public:
    using InvokeFuncStorage = void(*)();
    
    CallbackBase(InvokeFuncStorage func) : func_(func) {};
    
protected:
    InvokeFuncStorage func_;
};

template <typename R, typename... Args>
class Callback;

template <typename R, typename... Args>
class Callback<R(Args...)> : public CallbackBase {
public:
    using RunType = R(Args...);
    using PolymorphicInvoke = R (*)(Args...);
    
    Callback() : CallbackBase(nullptr) {};
    Callback(PolymorphicInvoke func) : CallbackBase((CallbackBase::InvokeFuncStorage)func) {}
    
    R run(Args... args) {
        auto func = reinterpret_cast<PolymorphicInvoke>(func_);
        return func(std::forward<Args>(args)...);
    }
};

如果处理回调的地方可以直接存储形如Callback<void(int)>强类型的实例,则问题解决,回调数据回来的时候直接调用callback.run(intData)即可。

但如果存储callback实例的是一个公共的地方(如一个CallbackManager类),且CallbackManager存储了各种类型的callback实例(各个callback实例的模板参数不同),该如何统一存储呢?存储CallbackBase能解决问题吗?在回调数据到达CallbackManager时,CallbackManager该如何调用回调呢?怎样才能知道该将CallbackBase cast成什么子类呢?

细化一下此问题:

  • CallbackManager需要存储各种模板类型的Callback实例
    • 比如其中有一个类型为Callback<void(int)>,即接收一个int参数,并且假设其callbackId为123
  • CallbackManager会在不同时机收到(callbackId,callbackData)的二元组,其中callbackData为序列化后的回调数据
    • 本例中序列化数据(callbackData)为string类型
  • CallbackManager需要根据callbackId(如123)找到相应的callback(此例中为Callback<void(int)>实例),并根据callback的模板参数类型(本例中为一个参数,int型)将callbackData(本例中为string类型)反序列化,并对其调用callback方法。

要解决此问题还需要多一层封装,即对每个Callback类型封装一个CallbackResponder类,如上例中的Callback<void(int>)需要被封装到一个MyCallbackResponder类中:

c 复制代码
class MyCallbackResponder : public CallbackResponder {
public:
    MyCallbackResponder(std::string callbackId, Callback<void(int)>callback) : callbackId_(callbackId), callback_(std::move(callback)) {}
    
    virtual void onMessage(const std::string &msg) override {
        //反序列化,把general消息转换为强类型参数x, y
        Serializer serializer(msg);
        int x = serializer.deserialize("x");
        
        //调用callback
        callback_.run(x);
    }
    
private:
    std::string callbackId_;
    Callback<void(int)> callback_;
};

其中 ICallbackResponder 形如:

csharp 复制代码
class CallbackResponder {
public:
    virtual void onMessage(std::string msg) = 0;
}

CallbackManager中存储的是 CallbackResponder 类型的实例列表:

c 复制代码
class CallbackManager {
public:
    void onCallback(const std::string &callbackId, const std::sring &callbackData) {
        if (responders_.find(callbackId) != responders_.end()) {
            responders_[callbackId]->onMessage(callbackData);
        }
    }


private:
    std::unordered_map<std::string, std::unique_ptr<CallbackResponder>> responders_;
}

CallbackManager 在 onCallback 方法中只要找到相应的 callbackResponder,然后调用其onMessage方法即可。

相关推荐
程序猿小D25 分钟前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
奔跑吧邓邓子1 小时前
npm包管理深度探索:从基础到进阶全面教程!
前端·npm·node.js
前端李易安1 小时前
ajax的原理,使用场景以及如何实现
前端·ajax·okhttp
汪子熙2 小时前
Angular 服务器端应用 ng-state tag 的作用介绍
前端·javascript·angular.js
Envyᥫᩣ2 小时前
《ASP.NET Web Forms 实现视频点赞功能的完整示例》
前端·asp.net·音视频·视频点赞
500了5 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵6 小时前
Android Debug Bridge(ADB)完全指南
android·adb
Мартин.6 小时前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
昨天;明天。今天。7 小时前
案例-表白墙简单实现
前端·javascript·css
数云界7 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端