You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++11环境下,MQTT扩展库如何存储任意类型回调函数?

基于C++11实现支持任意回调的MQTT命令库

要在C++11中实现支持任意返回类型、任意参数的回调命令存储与执行,核心思路是利用类型擦除封装不同签名的回调,结合模板和标准库工具实现参数解析与回调调用。以下是具体方案:

1. 核心设计思路

放弃直接在结构体中存储任意类型的函数指针和参数,改用:

  • 抽象基类统一所有回调的调用接口(类型擦除)
  • 模板派生类封装具体的回调函数和参数解析逻辑
  • 手动实现C++11缺失的index_sequence工具,用于展开tuple参数

2. 实现代码

第一步:补全C++11缺失的index_sequence工具

C++11没有std::index_sequence,需要手动实现:

// C++11兼容的index_sequence实现
template<std::size_t... Indices>
struct index_sequence {};

template<std::size_t N, std::size_t... Indices>
struct make_index_sequence_impl : make_index_sequence_impl<N-1, N-1, Indices...> {};

template<std::size_t... Indices>
struct make_index_sequence_impl<0, Indices...> : index_sequence<Indices...> {};

template<std::size_t N>
using make_index_sequence = make_index_sequence_impl<N>;

第二步:定义抽象回调基类

用类型擦除统一所有回调的执行接口:

#include <memory>
#include <string>

class CommandCallback {
public:
    virtual ~CommandCallback() = default;
    // 接收MQTT消息字符串,执行回调(若需返回值可修改为返回自定义Any类型)
    virtual void execute(const std::string& mqtt_msg) = 0;
};

第三步:实现模板化的具体回调类

封装具体的回调函数和参数解析逻辑,自动处理参数展开:

#include <functional>
#include <tuple>

template<typename Func, typename... Args>
class TypedCommandCallback : public CommandCallback {
public:
    // 构造函数:传入回调函数和MQTT消息到参数的解析器
    TypedCommandCallback(Func func, std::function<std::tuple<Args...>(const std::string&)> parser)
        : func_(std::move(func)), parser_(std::move(parser)) {}

    void execute(const std::string& mqtt_msg) override {
        // 解析MQTT消息为回调所需的参数tuple
        auto args_tuple = parser_(mqtt_msg);
        // 展开tuple并调用回调
        invoke_with_tuple(func_, args_tuple);
    }

private:
    Func func_;
    std::function<std::tuple<Args...>(const std::string&)> parser_;

    // 辅助函数:展开tuple参数调用函数
    template<typename F, typename Tuple, std::size_t... Indices>
    static void invoke_impl(F&& func, Tuple&& args, index_sequence<Indices...>) {
        std::forward<F>(func)(std::get<Indices>(std::forward<Tuple>(args))...);
    }

    template<typename F, typename Tuple>
    static void invoke_with_tuple(F&& func, Tuple&& args) {
        using TupleSize = typename std::decay<Tuple>::type;
        invoke_impl(
            std::forward<F>(func),
            std::forward<Tuple>(args),
            make_index_sequence<std::tuple_size<TupleSize>::value>()
        );
    }
};

第四步:定义Command结构体与命令管理

#include <vector>

struct Command {
    uint64_t alias;
    std::unique_ptr<CommandCallback> callback;
};

// 全局命令数组(实际项目建议封装为类成员)
std::vector<Command> g_commands;

// 模板注册函数:简化用户注册命令的流程
template<typename Func, typename... Args>
void register_mqtt_command(uint64_t alias, Func&& func, std::function<std::tuple<Args...>(const std::string&)> parser) {
    g_commands.push_back({
        alias,
        std::make_unique<TypedCommandCallback<Func, Args...>>(
            std::forward<Func>(func),
            std::move(parser)
        )
    });
}

3. 用户使用示例

示例1:普通函数回调

#include <iostream>

// 用户自定义回调
void print_integer(int num) {
    std::cout << "Received integer: " << num << std::endl;
}

// 自定义MQTT消息解析器:将字符串转为int
std::tuple<int> parse_int_msg(const std::string& msg) {
    return std::make_tuple(std::stoi(msg));
}

// 注册命令
int main() {
    register_mqtt_command(1001, print_integer, parse_int_msg);

    // 模拟收到MQTT消息并执行
    for (auto& cmd : g_commands) {
        if (cmd.alias == 1001) {
            cmd.callback->execute("12345");
        }
    }
    return 0;
}

示例2:成员函数回调

class Device {
public:
    void set_power(bool on) {
        std::cout << "Device power " << (on ? "ON" : "OFF") << std::endl;
    }
};

// 解析开关命令
std::tuple<bool> parse_power_msg(const std::string& msg) {
    return std::make_tuple(msg == "ON");
}

int main() {
    Device my_device;
    // 用std::bind绑定成员函数
    register_mqtt_command(1002, 
        std::bind(&Device::set_power, &my_device, std::placeholders::_1),
        parse_power_msg
    );

    // 模拟执行
    for (auto& cmd : g_commands) {
        if (cmd.alias == 1002) {
            cmd.callback->execute("ON");
        }
    }
    return 0;
}

示例3:无参数回调

void hello_world() {
    std::cout << "Hello World!" << std::endl;
}

// 无参数解析器返回空tuple
std::tuple<> parse_empty_msg(const std::string&) {
    return std::tuple<>();
}

int main() {
    register_mqtt_command(1003, hello_world, parse_empty_msg);
    // 模拟执行
    for (auto& cmd : g_commands) {
        if (cmd.alias == 1003) {
            cmd.callback->execute("");
        }
    }
    return 0;
}

4. 扩展说明

  • 返回值处理:若需获取回调返回值,可将CommandCallback::execute改为返回自定义的Any类型(C++11无std::any,需手动实现简单的类型擦除容器)。
  • 线程安全:若库涉及多线程调用,需对命令数组的读写操作加锁(如用std::mutex)。
  • 参数格式:参数解析逻辑由用户自定义,支持JSON、CSV等任意消息格式,灵活性更高。

内容的提问来源于stack exchange,提问作者J.Meulenbeld

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:18:37