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

如何运行时实例化带SinkType模板参数的Logger类并保留Concept约束

保留模板类Concept约束的运行时实例化方案

核心思路是用std::variant存储模板Logger的不同实例,配合模板转发保留Concept约束,同时通过工厂方法实现运行时根据配置选择SinkType。

步骤1:定义基础组件

首先定义你的StringLike Concept和Sink类型:

#include <variant>
#include <string>
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>

template<typename T>
concept StringLike = requires(T t) {
    { std::string(t) } -> std::convertible_to<std::string>;
};

// 文件Sink
struct FileSink {
    void Output(StringLike auto&& msg) {
        std::ofstream file("log.txt", std::ios::app);
        file << msg << '\n';
    }
};

// 控制台Sink
struct ConsoleSink {
    void Output(StringLike auto&& msg) {
        std::cout << msg << '\n';
    }
};

// 模板Logger实现类
template<typename SinkType>
class LoggerImpl {
public:
    void Log(StringLike auto&& msg) {
        sink_.Output(std::forward<decltype(msg)>(msg));
    }
private:
    SinkType sink_;
};

步骤2:封装运行时可选择的Logger类

用std::variant存储所有可能的LoggerImpl实例,通过工厂方法根据JSON配置创建对应实例,同时提供带StringLike约束的模板Log方法:

class Logger {
public:
    // 从JSON配置创建Logger实例
    static Logger CreateFromConfig(const nlohmann::json& config) {
        const std::string sink_type = config.at("sink_type");
        if (sink_type == "file") {
            return Logger(LoggerImpl<FileSink>{});
        } else if (sink_type == "console") {
            return Logger(LoggerImpl<ConsoleSink>{});
        }
        throw std::invalid_argument("Unsupported sink type: " + sink_type);
    }

    // 带StringLike约束的Log方法,编译时检查参数合法性
    template<StringLike T>
    void Log(T&& msg) {
        std::visit([&](auto& impl) {
            impl.Log(std::forward<T>(msg));
        }, impl_);
    }

private:
    // 存储所有可能的LoggerImpl实例
    std::variant<LoggerImpl<FileSink>, LoggerImpl<ConsoleSink>> impl_;

    // 私有构造函数,仅由工厂方法调用
    explicit Logger(decltype(impl_) impl) : impl_(std::move(impl)) {}
};

步骤3:使用示例

int main() {
    // 模拟从JSON读取配置
    nlohmann::json config = R"({"sink_type": "console"})"_json;
    
    Logger logger = Logger::CreateFromConfig(config);
    logger.Log("Hello, runtime sink!"); // 编译时检查是否符合StringLike
    logger.Log(123); // 编译报错,不符合StringLike约束,保留了原模板类的检查能力
    
    return 0;
}

方案优势

  • 保留了Log方法的StringLike Concept约束,编译时就能拦截非法参数,不会像动态多态那样丢失编译期检查
  • 通过std::variant和工厂方法实现了运行时选择SinkType的能力,无需放弃模板类的设计
  • 性能接近直接使用模板类,std::visit在大多数情况下会被编译器优化为分支判断,开销极小

扩展场景

如果后续需要新增Sink类型,只需:

  1. 定义新的Sink结构体
  2. 将LoggerImpl<NewSink>添加到std::variant的类型列表中
  3. 在CreateFromConfig中添加对应的分支判断即可

内容的提问来源于stack exchange,提问作者user7854092

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:10:33