如何在C++宏中用std::source_location替代__LINE__与__FUNCSIG__?
解决方案
问题核心是预处理宏的Token粘贴(##)只能处理编译期常量,而std::source_location::current().line()是运行时函数调用,无法在预处理阶段求值,所以不能直接用来生成变量名。我们可以通过以下两步解决:
1. 修改ProfileLogScope类适配std::source_location
为类添加带std::source_location默认参数的构造函数,让它自动捕获宏调用位置的信息:
#include <source_location> class ProfileLogScope { public: // 新增构造函数,默认参数自动捕获调用点的source_location ProfileLogScope(const char* tag, std::source_location loc = std::source_location::current()) : m_tag(tag), m_loc(loc) { // 示例初始化逻辑:记录tag、文件、行号、函数名 // log("Start scope: %s | %s:%d | %s", tag, loc.file_name(), loc.line(), loc.function_name()); } ~ProfileLogScope() { // 示例结束逻辑:记录耗时等 // log("End scope: %s", m_tag); } private: const char* m_tag; std::source_location m_loc; };
2. 重新定义宏解决变量名和参数传递问题
用预处理阶段的递增计数器__COUNTER__替代__LINE__生成唯一变量名(比__LINE__更可靠,同一行多次调用宏也不会重复),同时利用std::source_location的默认参数或直接获取函数名:
#define TOKEN_PASTE_SIMPLE(x, y) x##y #define TOKEN_PASTE(x, y) TOKEN_PASTE_SIMPLE(x, y) #define TOKEN_STRINGIZE_SIMPLE(x) #x #define TOKEN_STRINGIZE(x) TOKEN_STRINGIZE_SIMPLE(x) #if defined PROFILE_LOG_SCOPE || defined PROFILE_LOG_SCOPE_FUNCTION #undef PROFILE_LOG_SCOPE #undef PROFILE_LOG_SCOPE_FUNCTION #endif #ifdef PROFILE_BUILD // 带自定义标签的版本,默认参数自动捕获位置 #define PROFILE_LOG_SCOPE(tag_str) \ ProfileLogScope TOKEN_PASTE(plscope_, __COUNTER__)(tag_str) // 自动用函数名作为标签的版本 #define PROFILE_LOG_SCOPE_FUNCTION() \ ProfileLogScope TOKEN_PASTE(plscope_, __COUNTER__)(std::source_location::current().function_name()) #else #define PROFILE_LOG_SCOPE(tag_str) #define PROFILE_LOG_SCOPE_FUNCTION() #endif
关键说明
- 变量名生成:
__COUNTER__是预处理阶段的内置宏,每次宏展开都会生成唯一递增数值,完美避开了运行时函数调用无法参与Token粘贴的问题。 - 位置捕获:构造函数的
std::source_location::current()默认参数会在宏调用的位置执行,精准捕获当前文件、行号、函数名等信息,符合调试需求。 - 兼容性:
std::source_location是C20标准特性,编译时需开启C20支持(如GCC用-std=c++20,MSVC用/std:c++20)。
内容的提问来源于stack exchange,提问作者Casey
相关产品推荐
相关产品推荐

