C++头文件日志函数报多重定义错误及日志功能扩展咨询
多重定义报错原因
报错和Makefile配置无关,根源是头文件中4个无参的日志基础函数:
void logInfo(){} void logWarning(){} void logError(){} void logDebug(){}
这几个是普通非模板函数,没有链接级别的重复定义豁免属性。当多个.cpp源文件包含这个头文件时,每个编译单元都会独立生成一份这四个函数的实现,链接阶段就会触发重复定义错误。你写的可变参数模板函数不会触发这个问题,因为C++中模板函数默认是隐式inline的,链接器会自动合并重复定义,不会冲突。
修复方案
二选一即可:
- 给4个无参函数加上
inline关键字,C++标准规定inline函数允许在不同编译单元存在相同定义,链接器不会报错:
inline void logInfo(){} inline void logWarning(){} inline void logError(){} inline void logDebug(){}
- 也可以删掉这几个无参的空实现,改用C++17的
if constexpr判断参数包长度做递归终止,能减少冗余代码。
增加文件名、函数名、行号的实现方法
不需要完全重写现有代码,加一层宏封装即可。注意__FILE__、__FUNCTION__、__LINE__是预处理器内置宏,必须在用户调用日志的位置展开才能拿到正确值,如果直接写在日志函数内部,拿到的永远是日志头文件内的位置,不是实际调用点。
修改后的头文件示例:
#include <iostream> #include <utility> // 递归终止的空函数,加inline避免重定义 inline void logInfo(){} inline void logWarning(){} inline void logError(){} inline void logDebug(){} // 实际执行打印的内部实现,新增位置参数 template<typename First, typename ...Rest> void logInfoImpl(const char* file, const char* func, int line, First&& first, Rest&&... rest) { std::cout << "[INFO] [" << file << ":" << line << " " << func << "] " << std::forward<First>(first) << '\n'; logInfo(std::forward<Rest>(rest)...); } template<typename First, typename ...Rest> void logWarningImpl(const char* file, const char* func, int line, First&& first, Rest&&... rest) { std::cout << "[WARNING] [" << file << ":" << line << " " << func << "] " << std::forward<First>(first) << '\n'; logWarning(std::forward<Rest>(rest)...); } template<typename First, typename ...Rest> void logErrorImpl(const char* file, const char* func, int line, First&& first, Rest&&... rest) { std::cout << "[ERROR] [" << file << ":" << line << " " << func << "] " << std::forward<First>(first) << '\n'; logError(std::forward<Rest>(rest)...); } template<typename First, typename ...Rest> void logDebugImpl(const char* file, const char* func, int line, First&& first, Rest&&... rest) { std::cout << "[DEBUG] [" << file << ":" << line << " " << func << "] " << std::forward<First>(first) << '\n'; logDebug(std::forward<Rest>(rest)...); } // 对外暴露的宏,自动注入调用位置的宏参数 #define logInfo(...) logInfoImpl(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) #define logWarning(...) logWarningImpl(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) #define logError(...) logErrorImpl(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) #define logDebug(...) logDebugImpl(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
原有调用代码不需要任何修改,编译后日志会自动带上调用位置的文件名、函数名、行号信息。
内容的提问来源于stack exchange,提问作者L. Kicki
相关产品推荐
相关产品推荐

