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

利用可变参数模板与元组实现带默认参数的编译期日志定义

实现带编译期默认参数的日志定义

要让未传入的参数使用预设默认值且所有处理在编译期完成,我们可以通过编译期元组补全和constexpr工具函数来实现,以下是修改后的完整代码及说明:

修改后的代码

#include <iostream>
#include <tuple>
#include <memory>
#include <type_traits>
#include <utility>

class MyLogger {
public:
    template<typename... Args>
    void Log(const char* format, Args... args) {
        printf(format, args...);
        printf("\n");
    }
};

static inline std::unique_ptr<MyLogger> logger = std::make_unique<MyLogger>();

// 编译期获取类型的预设默认值
template<typename T>
constexpr T default_value() {
    if constexpr (std::is_same_v<T, int>) {
        return 0;
    } else if constexpr (std::is_same_v<T, float>) {
        return 0.0f;
    }
    // 可扩展其他类型的默认值,比如bool返回false,std::string返回空串等
    else {
        return T{}; // 基础类型的零初始化
    }
}

template<typename FMT, typename... RequiredArgs>
struct LogLineDef {
    // 支持传入部分参数,自动补全默认值
    template<typename... ProvidedArgs>
    constexpr LogLineDef(ProvidedArgs... args) 
        : values(complete_tuple(std::make_tuple(std::forward<ProvidedArgs>(args)...))) {}

    std::tuple<RequiredArgs...> values;

private:
    // 编译期补全元组的核心实现
    template<typename Tuple, size_t... ProvIdx, size_t... RemIdx>
    static constexpr std::tuple<RequiredArgs...> complete_tuple_impl(Tuple&& prov_tuple, 
                                                                      std::index_sequence<ProvIdx...>, 
                                                                      std::index_sequence<RemIdx...>) {
        return std::make_tuple(
            // 取传入的参数
            std::get<ProvIdx>(std::forward<Tuple>(prov_tuple))...,
            // 补全剩余参数的默认值
            default_value<std::tuple_element_t<sizeof...(ProvIdx) + RemIdx, std::tuple<RequiredArgs...>>>()...
        );
    }

    template<typename... ProvidedArgs>
    static constexpr std::tuple<RequiredArgs...> complete_tuple(std::tuple<ProvidedArgs...> provided) {
        // 静态断言:传入参数不能超过要求的数量
        static_assert(sizeof...(ProvidedArgs) <= sizeof...(RequiredArgs), 
                      "Number of provided arguments exceeds required count");
        
        using ProvidedSeq = std::make_index_sequence<sizeof...(ProvidedArgs)>;
        using RemainingCount = std::make_index_sequence<sizeof...(RequiredArgs) - sizeof...(ProvidedArgs)>;
        return complete_tuple_impl(std::move(provided), ProvidedSeq{}, RemainingCount{});
    }
};

constexpr char FMT1[] = "A:%d, B:%d, C:%d, D:%f ";
using L1 = LogLineDef<std::integral_constant<const char*, FMT1>, int, int, int, float>;

template<typename FMT, typename... Args>
void log_helper(const char* message, const LogLineDef<FMT, Args...>& logger_line_def) {
    std::apply([message](const auto&... args) {
        logger->Log((std::string(FMT::value) + message).c_str(), args...);
    }, logger_line_def.values);
}

#define LOG(logger_line_def, message) log_helper(message, logger_line_def)

int main() {
    LOG(L1(1, 2, 3, 4.f), "error"); // 全参数传入,正常输出
    LOG(L1(1, 2, 3), "error");      // 自动补全D的默认值0.0f
    LOG(L1(1, 2), "error");         // 自动补全C的默认值0,D的默认值0.0f
}

关键实现说明

  1. 编译期默认值生成:
    default_value函数通过if constexpr在编译期为不同类型返回预设默认值,完全避免运行期计算。

  2. 元组补全逻辑:

    • 构造函数接收任意数量的前N个参数,转为临时元组
    • 通过std::index_sequence编译期计算剩余参数数量,自动补全对应类型的默认值,生成符合RequiredArgs要求的完整元组
    • 静态断言防止传入过多参数,提前在编译期暴露错误
  3. 编译期保证:
    所有元组操作、默认值计算均为constexpr,编译器会在编译阶段完成所有补全逻辑,运行期直接使用预先生成的完整参数元组,无额外性能开销。

输出结果

运行上述代码会输出:

A:1, B:2, C:3, D:4.000000 error
A:1, B:2, C:3, D:0.000000 error
A:1, B:2, C:0, D:0.000000 error

内容的提问来源于stack exchange,提问作者Enes Aygün

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 20:38:13