C++17中获取无参数类名+函数名的通用方法(兼容多场景)
解决方案:基于
__PRETTY_FUNCTION__的可靠函数名提取 由于C++17没有标准的函数名反射特性,我们可以基于编译器内置的__PRETTY_FUNCTION__宏做解析——它能提供包含函数完整签名的字符串,我们只需要从中提取目标格式的函数名。
核心思路
- 从
__PRETTY_FUNCTION__中剥离返回值、模板参数、函数参数部分 - 保留类(含嵌套类)与函数名的层级关系,最终输出
class_name::func_name()或func_name()格式
实现代码
以下是适配所有要求场景的实现,支持GCC/Clang/MSVC主流编译器:
#include <string_view> #include <algorithm> #include <cctype> namespace detail { // 跳过返回值部分,定位到函数名起始位置 constexpr std::string_view skip_return_type(std::string_view pretty_func) { // 适配MSVC的调用约定前缀 if (pretty_func.find("__cdecl ") != std::string_view::npos) { pretty_func.remove_prefix(pretty_func.find("__cdecl ") + 8); } else if (pretty_func.find("__stdcall ") != std::string_view::npos) { pretty_func.remove_prefix(pretty_func.find("__stdcall ") + 9); } else if (pretty_func.find("__thiscall ") != std::string_view::npos) { pretty_func.remove_prefix(pretty_func.find("__thiscall ") + 10); } // 适配GCC/Clang格式,找到函数名前的最后一个空格 size_t func_start = pretty_func.find_last_of(' ') + 1; if (func_start != std::string_view::npos) { pretty_func = pretty_func.substr(func_start); } return pretty_func; } // 剥离模板参数(处理嵌套模板的情况) constexpr std::string_view strip_template_args(std::string_view func_part) { size_t template_start = func_part.find('<'); if (template_start == std::string_view::npos) { return func_part; } size_t template_depth = 1; size_t pos = template_start + 1; while (pos < func_part.size() && template_depth > 0) { if (func_part[pos] == '<') { ++template_depth; } else if (func_part[pos] == '>') { --template_depth; } ++pos; } return func_part.substr(0, template_start); } // 剥离函数参数(处理参数内包含()的情况,如std::function<void()>) constexpr std::string_view strip_function_args(std::string_view func_part) { size_t arg_start = func_part.find('('); if (arg_start == std::string_view::npos) { return func_part; } size_t arg_depth = 1; size_t pos = arg_start + 1; while (pos < func_part.size() && arg_depth > 0) { if (func_part[pos] == '(') { ++arg_depth; } else if (func_part[pos] == ')') { --arg_depth; } ++pos; } return func_part.substr(0, arg_start + 1) + ")"; } } // 对外接口宏,生成目标格式的函数名字符串 #define GET_FUNC_NAME() []() constexpr { \ auto pretty = std::string_view(__PRETTY_FUNCTION__); \ auto after_return = detail::skip_return_type(pretty); \ auto no_template = detail::strip_template_args(after_return); \ auto no_args = detail::strip_function_args(no_template); \ return no_args; \ }() // 测试用例 #include <iostream> #include <functional> namespace ns { class Outer { public: class Nested { public: void instance_method(std::function<void()>) {} static void static_method(int) {} template <typename T> T template_method(T val) { return val; } }; static void outer_static() {} }; } void c_style_func(double) {} template <typename T> void template_free_func(T) {} int main() { ns::Outer::Nested nested; std::cout << GET_FUNC_NAME() << "\n"; // 输出 main() c_style_func(3.14); std::cout << GET_FUNC_NAME() << "\n"; // 输出 c_style_func() nested.instance_method([](){}); std::cout << GET_FUNC_NAME() << "\n"; // 输出 ns::Outer::Nested::instance_method() ns::Outer::Nested::static_method(42); std::cout << GET_FUNC_NAME() << "\n"; // 输出 ns::Outer::Nested::static_method() nested.template_method(123); std::cout << GET_FUNC_NAME() << "\n"; // 输出 ns::Outer::Nested::template_method() ns::Outer::outer_static(); std::cout << GET_FUNC_NAME() << "\n"; // 输出 ns::Outer::outer_static() template_free_func("test"); std::cout << GET_FUNC_NAME() << "\n"; // 输出 template_free_func() return 0; }
适配场景说明
- 模板函数:通过统计
<>的嵌套深度,精准剥离模板参数部分,仅保留函数名 - 含()的参数:统计
()的嵌套深度,正确定位函数参数的结束位置,不会误截断 - 静态/实例方法:
__PRETTY_FUNCTION__会保留完整的类层级,解析后自然输出class::func()格式 - 嵌套类:支持多层嵌套类的函数名提取,如
ns::Outer::Nested::func()
编译器兼容性
- GCC 7+:完全支持
- Clang 6+:完全支持
- MSVC 2019+:需开启
/Zc:__cplusplus启用C++17特性,解析逻辑已适配MSVC的__PRETTY_FUNCTION__格式
内容的提问来源于stack exchange,提问作者Dachuan Huang
相关产品推荐
相关产品推荐

