如何实现自动切换cout/printf的C++可变参数LOG宏
实现支持双模式的C++ LOG宏
核心思路
要实现同时支持流式输出(<<)和格式化输出(类似printf)的LOG宏,核心是利用函数重载区分不同调用场景,配合宏的参数个数判断来分发到对应的处理逻辑:
- 无参数或单个参数(包括流式表达式):转发给
std::cout处理 - 多参数(格式串+变量):转发给
printf处理
完整实现代码
#include <iostream> #include <cstdio> #include <utility> #include <type_traits> // 流式输出辅助类:负责处理LOG << ... << ...的链式调用 class LogHelper { public: template<typename T> LogHelper& operator<<(T&& val) { std::cout << std::forward<T>(val); return *this; } // 析构时自动添加换行,统一输出格式 ~LogHelper() { std::cout << '\n'; } }; // 处理多参数场景:格式串+变量,转发给printf template<typename... Args, typename = std::enable_if_t<(sizeof...(Args) > 0)>> void log_impl(const char* fmt, Args&&... args) { printf(fmt, std::forward<Args>(args)...); putchar('\n'); } // 处理单个字符串参数:直接用cout输出 void log_impl(const char* msg) { std::cout << msg << '\n'; } // 处理单个非字符串参数:比如数字、对象等,用cout输出 template<typename T> std::enable_if_t<!std::is_same_v<std::decay_t<T>, const char*>, void> log_impl(T&& val) { std::cout << std::forward<T>(val) << '\n'; } // 处理无参数场景:返回LogHelper对象,支持链式<<调用 LogHelper log_impl() { return LogHelper(); } // 宏分发逻辑:根据参数个数选择对应的处理函数 #define LOG(...) _LOG_DISPATCH(__VA_ARGS__) #define _LOG_DISPATCH(...) \ _LOG_IF_HAS_ARGS(__VA_ARGS__, _LOG_HAS_ARGS, _LOG_NO_ARGS)(__VA_ARGS__) #define _LOG_IF_HAS_ARGS(_1, ...) _LOG_HAS_ARGS #define _LOG_HAS_ARGS(...) log_impl(__VA_ARGS__) #define _LOG_NO_ARGS() log_impl()
测试用例
int main() { // 单个字符串参数:用cout输出 LOG("This is a single message"); // 多参数格式化:用printf输出 LOG("Number: %d, String: %s", 123, "test"); // 流式调用:用cout输出,自动加换行 LOG << "Stream" << " output" << ", number: " << 456; // 单个非字符串参数:用cout输出 LOG(789); return 0; }
关键细节说明
- 参数个数判断:通过宏
_LOG_IF_HAS_ARGS判断是否有传入参数,无参数时返回LogHelper对象支持流式调用,有参数时转发给对应的log_impl重载。 - 重载区分:
- 多参数版本通过
std::enable_if限制仅当参数个数>0时生效,避免和单个字符串参数的重载冲突。 - 单个参数版本区分字符串和非字符串类型,确保都用
cout输出。
- 多参数版本通过
- 自动换行:流式调用通过
LogHelper的析构函数自动添加换行,格式化调用则手动添加,保证所有输出风格统一。
内容的提问来源于stack exchange,提问作者XORer
相关产品推荐
相关产品推荐

