You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++17中获取无参数类名+函数名的通用方法(兼容多场景)

解决方案:基于__PRETTY_FUNCTION__的可靠函数名提取

由于C++17没有标准的函数名反射特性,我们可以基于编译器内置的__PRETTY_FUNCTION__宏做解析——它能提供包含函数完整签名的字符串,我们只需要从中提取目标格式的函数名。

核心思路

  1. 从__PRETTY_FUNCTION__中剥离返回值、模板参数、函数参数部分
  2. 保留类(含嵌套类)与函数名的层级关系,最终输出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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 11:43:13