如何在无宏情况下结合std::source_location与可变参数实现日志
解决std::source_location与可变参数日志结合的位置错误问题
你的问题核心在于std::source_location的默认参数位置放错了——你把它放在底层_log函数里,而_log是被log模板函数调用的,所以std::source_location::current()会捕获log函数内部的调用位置,而非原始的日志调用点。
不用宏的解决方案很直接:把std::source_location的默认参数直接放到可变参数的log模板函数中,让它在日志的调用位置求值,再传递给底层函数。
修改后的完整代码
#include <source_location> #include <string> #include <string_view> #include <iostream> #include <fmt/format.h> // 底层日志实现,接收已格式化的字符串和source_location bool _log(std::string strFormatedDebugMessage, const std::source_location loc); // 可变参数模板的日志入口,将source_location默认参数放在这里 template<typename ...Args> bool log(fmt::format_string<Args...> fmt, Args&&... args, const std::source_location loc = std::source_location::current()) { // 正确转发参数并格式化(你之前的代码未处理args,此处修正) std::string formatted_msg = fmt::format(fmt, std::forward<Args>(args)...); return _log(std::move(formatted_msg), loc); } bool _log(std::string strFormatedDebugMessage, const std::source_location loc) { // 现在会正确显示原始调用log的行号 std::cout << "Called from Line " << loc.line() << std::endl; std::cout << "Log: " << strFormatedDebugMessage << std::endl; // 实际写入硬件的逻辑(失败时返回false) return true; } int main() { bool bRes = log("Log with format {} {} value", "Some dummy", 1); // 捕获此行的行号 bRes &= log("Log without format args"); // 捕获此行的行号 std::cout << "Hello World" << bRes << std::endl; }
关键说明
- source_location的位置:必须将
std::source_location loc = std::source_location::current()作为log函数的最后一个参数(C++20允许默认参数放在可变参数包之后)。这样默认参数的求值会发生在log的调用点(比如main里的两行log调用),而非内部调用_log的位置。 - 参数转发与格式化:用
std::forward<Args>(args)...完美转发参数,确保fmt格式化的正确性,同时保留参数的原有属性。 - 类型安全:
fmt::format_string<Args...>保证格式字符串和参数类型匹配,编译期就能检查错误,比宏实现的类型安全性更高。
这样修改后,loc.line()会正确显示main函数中调用log的行号,完全不需要宏就能实现需求。
内容的提问来源于stack exchange,提问作者user8694474
相关产品推荐
相关产品推荐

