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

如何基于STL实现更通用的C++发布-订阅模式?

通用C++发布-订阅与查询模式实现(基于STL)

核心思路

利用std::function和lambda捕获智能指针替代传统接口类继承,实现灵活的回调绑定;通过模板支持任意参数数量与类型;用std::async实现异步转发;单独封装查询类处理一对一请求-响应场景。


1. 存储订阅者与成员函数的组合并完成注册

发布者通过模板成员函数接收订阅者智能指针和成员函数指针,将二者绑定为std::function对象存储。lambda捕获智能指针可保证订阅者生命周期与回调绑定周期一致,避免悬空指针问题。

关键实现代码:

template<typename... Args>
class Publisher {
private:
    using Callback = std::function<void(Args...)>;
    std::unordered_map<std::string, std::vector<Callback>> channels_;

public:
    // 注册订阅者:智能指针 + 成员函数指针
    template<typename SubscriberType>
    void subscribe(const std::string& channel, 
                   std::shared_ptr<SubscriberType> subscriber, 
                   void(SubscriberType::*callback)(Args...)) {
        channels_[channel].emplace_back(
            [subscriber, callback](Args... args) {
                // 完美转发参数调用成员函数
                (subscriber.get()->*callback)(std::forward<Args>(args)...);
            }
        );
    }
};

2. 发布时绑定具体参数

发布时遍历对应通道的回调列表,通过完美转发将参数传递给每个回调,保证参数的原始值类别(左值/右值),避免不必要的拷贝。

添加发布方法到Publisher类:

void publish(const std::string& channel, Args... args) {
    auto channel_it = channels_.find(channel);
    if (channel_it == channels_.end()) return;

    for (auto& callback : channel_it->second) {
        callback(std::forward<Args>(args)...);
    }
}

3. 扩展为异步版本

利用std::async以异步方式启动每个回调任务,无需手动管理线程。若需等待异步任务完成,可返回std::future集合;若只需后台执行,直接启动异步任务即可。

添加异步发布方法到Publisher类:

void publish_async(const std::string& channel, Args... args) {
    auto channel_it = channels_.find(channel);
    if (channel_it == channels_.end()) return;

    for (auto& callback : channel_it->second) {
        // 异步启动回调,无需等待结果
        std::async(std::launch::async, callback, std::forward<Args>(args)...);
    }
}

4. 实现查询版本(请求-响应模式)

查询模式为一对一的请求-响应场景,需存储单一提供者的回调并返回结果。同样用std::function绑定智能指针与成员函数,支持同步/异步查询。

实现查询提供者类:

template<typename ReturnType, typename... Args>
class QueryProvider {
private:
    using ProviderCallback = std::function<ReturnType(Args...)>;
    ProviderCallback provider_;

public:
    // 注册提供者:智能指针 + 返回值型成员函数指针
    template<typename ProviderType>
    void register_provider(std::shared_ptr<ProviderType> provider, 
                           ReturnType(ProviderType::*callback)(Args...)) {
        provider_ = [provider, callback](Args... args) -> ReturnType {
            return (provider.get()->*callback)(std::forward<Args>(args)...);
        };
    }

    // 同步查询
    ReturnType query(Args... args) {
        if (!provider_) {
            throw std::runtime_error("No provider registered for query");
        }
        return provider_(std::forward<Args>(args)...);
    }

    // 异步查询,返回future获取结果
    std::future<ReturnType> query_async(Args... args) {
        if (!provider_) {
            throw std::runtime_error("No provider registered for query");
        }
        return std::async(std::launch::async, provider_, std::forward<Args>(args)...);
    }
};

完整程序实现与测试

#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <vector>
#include <future>
#include <stdexcept>
#include <chrono>
#include <thread>

// 通用发布者类
template<typename... Args>
class Publisher {
private:
    using Callback = std::function<void(Args...)>;
    std::unordered_map<std::string, std::vector<Callback>> channels_;

public:
    template<typename SubscriberType>
    void subscribe(const std::string& channel, 
                   std::shared_ptr<SubscriberType> subscriber, 
                   void(SubscriberType::*callback)(Args...)) {
        channels_[channel].emplace_back(
            [subscriber, callback](Args... args) {
                (subscriber.get()->*callback)(std::forward<Args>(args)...);
            }
        );
    }

    void publish(const std::string& channel, Args... args) {
        auto channel_it = channels_.find(channel);
        if (channel_it == channels_.end()) return;

        for (auto& callback : channel_it->second) {
            callback(std::forward<Args>(args)...);
        }
    }

    void publish_async(const std::string& channel, Args... args) {
        auto channel_it = channels_.find(channel);
        if (channel_it == channels_.end()) return;

        for (auto& callback : channel_it->second) {
            std::async(std::launch::async, callback, std::forward<Args>(args)...);
        }
    }
};

// 通用查询提供者类
template<typename ReturnType, typename... Args>
class QueryProvider {
private:
    using ProviderCallback = std::function<ReturnType(Args...)>;
    ProviderCallback provider_;

public:
    template<typename ProviderType>
    void register_provider(std::shared_ptr<ProviderType> provider, 
                           ReturnType(ProviderType::*callback)(Args...)) {
        provider_ = [provider, callback](Args... args) -> ReturnType {
            return (provider.get()->*callback)(std::forward<Args>(args)...);
        };
    }

    ReturnType query(Args... args) {
        if (!provider_) {
            throw std::runtime_error("No provider registered for query");
        }
        return provider_(std::forward<Args>(args)...);
    }

    std::future<ReturnType> query_async(Args... args) {
        if (!provider_) {
            throw std::runtime_error("No provider registered for query");
        }
        return std::async(std::launch::async, provider_, std::forward<Args>(args)...);
    }
};

// 测试用订阅者类
class MessageSubscriber {
public:
    void receiveInChannel1(const std::string& msg, int priority) {
        std::cout << "Subscriber 1 received: [" << priority << "] " << msg << std::endl;
    }
};

// 测试用查询提供者类
class ConfigProvider {
public:
    std::string getConfigValue(const std::string& key) {
        if (key == "log_level") return "INFO";
        if (key == "max_connection") return "100";
        return "unknown";
    }
};

int main() {
    // 测试发布-订阅
    Publisher<const std::string&, int> msg_publisher;
    auto subscriber = std::make_shared<MessageSubscriber>();
    msg_publisher.subscribe("channel1", subscriber, &MessageSubscriber::receiveInChannel1);

    std::cout << "=== 同步发布 ===" << std::endl;
    msg_publisher.publish("channel1", "Hello Sync", 1);

    std::cout << "=== 异步发布 ===" << std::endl;
    msg_publisher.publish_async("channel1", "Hello Async", 2);
    // 等待异步任务完成(仅用于测试,实际业务无需)
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 测试查询
    QueryProvider<std::string, const std::string&> config_query;
    auto config_provider = std::make_shared<ConfigProvider>();
    config_query.register_provider(config_provider, &ConfigProvider::getConfigValue);

    std::cout << "=== 同步查询 ===" << std::endl;
    try {
        std::cout << "log_level: " << config_query.query("log_level") << std::endl;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    std::cout << "=== 异步查询 ===" << std::endl;
    try {
        auto future_val = config_query.query_async("max_connection");
        std::cout << "max_connection: " << future_val.get() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

内容的提问来源于stack exchange,提问作者Jörg Brüggmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:47:39