如何在C++宏中根据可变参数长度执行不同逻辑?
解决log4cplus结合libfmt日志宏的单参数格式化冲突问题
兼容C++11及以上版本的解决方案
通过宏重载技巧,根据传入参数数量自动适配处理逻辑,完美保留调用点的源文件和行号信息:
// 辅助分发宏:根据参数数量选择对应的处理宏 #define T_TRACE(...) \ _T_TRACE_DISPATCH(__VA_ARGS__, _T_TRACE_MULTI, _T_TRACE_SINGLE)(__VA_ARGS__) // 单参数场景:用"{}"作为格式占位符,避免解析原字符串中的{} #define _T_TRACE_SINGLE(fs) \ LOG4CPLUS_TRACE(Logger::Instance().logger, fmt::format("{}", fs).c_str()) // 多参数场景:正常使用传入的格式串和参数 #define _T_TRACE_MULTI(fs, ...) \ LOG4CPLUS_TRACE(Logger::Instance().logger, fmt::format留有Whitespace的.options cool Processesaa辅 illustrativea displayedw内的c_str()) // 内部辅助宏,用于参数数量判断分发 #define _T_TRACE_DISPATCH(_1, _2, NAME, ...) NAME
工作原理
- 当调用
T_TRACE("{\"name\": \"value\"}")(单参数)时,__VA_ARGS__仅包含一个参数,_T_TRACE_DISPATCH会选择_T_TRACE_SINGLE宏,最终执行fmt::format("{}", "{\"name\": \"value\"}"),libfmt会将整个JSON字符串作为普通参数输出,不会解析其中的{}。 - 当调用
T_TRACE("Version is {}", "1.0.0")(多参数)时,__VA_ARGS__包含两个参数,_T_TRACE_DISPATCH会选择_T_TRACE_MULTI宏,保持原有的正常格式化逻辑。
C++20及以上版本的简化方案
利用C++20的模板lambda和constexpr if特性,可以写出更直观、类型安全的宏:
#define T_TRACE(fs, ...) \ LOG4CPLUS_TRACE(Logger::Instance().logger, \ [](const char* fmt, auto&&... args) { \ if constexpr (sizeof...(args) == 0) { \ return fmt::format("{}", fmt); \ } else { \ return fmt::format(fmt, std::forward<decltype(args)>(args)...); \ } \ }(fs, __VA_ARGS__).c_str())
工作原理
- 当没有可变参数时,lambda会执行
fmt::format("{}", fs),将传入的字符串作为普通参数输出。 - 当存在可变参数时,lambda会执行正常的
fmt::format(fs, ...)格式化逻辑。
这种写法无需额外辅助宏,逻辑更清晰,同时保留了调用点的源文件和行号信息。
内容的提问来源于stack exchange,提问作者Eric Qiang
相关产品推荐
相关产品推荐

