std::source_location::function_name()不能直接获取干净函数名,返回的是含返回类型和参数的编译器符号名(如"void foo()"或"_Z3foov"),需手动解析提取;典型做法是find/substr跳过返回类型与括号间内容,注意优化下可能为空、MSVC含调用约定、宏中须用current()确保位置准确。

std::source_location 是什么,它能直接拿到函数名吗
不能。std::source_location::function_name() 返回的是编译器生成的符号名(比如 "void foo()" 或 "_Z3foov"),不是干净的函数名(如 "foo")。它依赖编译器实现,GCC/Clang 返回带签名的字符串,MSVC 返回类似 "void __cdecl foo(void)" 的格式——不是你日志里想看到的样子。
如何从 function_name() 中提取干净的函数名
得自己解析。常见做法是用字符串查找跳过返回类型和参数部分。注意:这不是标准行为,但对 GCC/Clang/MSVC 的典型输出都有效。
示例提取逻辑:
std::string clean_function_name(const std::source_location& loc) {
std::string full = loc.function_name();
// 找到第一个空格(返回类型后)和左括号(参数开始)
size_t start = full.find_last_of(" \t\n\r") + 1;
size_t end = full.find('(', start);
if (end != std::string::npos) {
return full.substr(start, end - start);
}
return full.substr(start); // fallback(极少见)
}要点:
立即学习“C++免费学习笔记(深入)”;
-
function_name()在优化开启(-O2)时可能为空或不可靠,调试时建议加-g并禁用内联:-fno-inline - 不要用
std::regex解析——启动开销大,且不必要;简单find/substr足够 - MSVC 的
function_name()可能含"__cdecl"等调用约定,需额外处理(如删掉"__cdecl"、"__stdcall")
在日志宏中安全集成 source_location
必须用 std::source_location::current() 作为默认参数,否则位置信息会固定在宏定义处,而非调用处。
正确写法:
#define LOG_DEBUG(msg) do { \
auto loc = std::source_location::current(); \
std::cerr << "[" << clean_function_name(loc) << ":" \
<< loc.line() << "] " << msg << "\n"; \
} while(0)关键点:
- 不能把
std::source_location::current()写成宏参数默认值(C++20 不支持宏参数默认值),必须在宏体内调用 - 避免在多线程环境直接用
std::cerr;若需线程安全,应转到线程局部缓冲或异步日志器 - 频繁调用
function_name()会产生小字符串临时对象,高频日志场景建议缓存或条件启用(如只在DEBUG下解析)
替代方案:为什么有时 __func__ 更实用
如果只要函数名,不用文件/行号,__func__ 更轻量、更稳定:__func__ 是字符串字面量,零开销,且所有编译器都保证只返回纯函数名(如 "foo")。
对比:
-
__func__:无运行时开销,无解析风险,但无法获取文件、行号、列号 -
std::source_location:提供完整上下文,但有字符串构造+解析成本,且function_name()格式不统一 - 混合用法常见:用
__func__做函数名,用std::source_location补充位置信息(file_name(),line())
真正要小心的是,别指望 function_name() 在所有构建配置下都返回一致结果——Release 模式下它可能为空,或者被链接器 strip 掉。调试阶段可用,上线前务必验证实际输出。


















