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

编译时从函数指针模板参数中获取目标函数名称

获取模板参数函数指针的函数名(C++20跨平台方案)

问题概述

你希望通过模板参数传入函数指针,在编译期或运行时获取该函数的名称或完整签名,当前使用C20,支持macOS、Linux、Windows,允许使用编译器扩展。这一需求确实属于**C反射特性**的范畴——目前C20及更早标准没有原生支持这类能力,需要依赖编译器扩展或平台API实现,未来的C反射标准会原生提供这类元信息获取能力。

编译期方案(consteval,依赖编译器扩展)

利用GCC/Clang的__PRETTY_FUNCTION__或MSVC的__FUNCSIG__,结合字符串解析提取函数名,完全在编译期完成计算:

#include <string_view>
#include <algorithm>

template <class T, T (*Fn)(T)>
struct Foo
{
    static consteval std::string_view printFnName()
    {
#ifdef __GNUC__ || __clang__
        constexpr std::string_view pretty_func = __PRETTY_FUNCTION__;
        // GCC/Clang格式示例:"static consteval std::string_view Foo<T, Fn>::printFnName() [with T = int; T (*Fn)(T) = bar]"
        constexpr auto start = pretty_func.find("= ") + 2;
        constexpr auto end = pretty_func.find(']', start);
        return pretty_func.substr(start, end - start);
#elif _MSC_VER
        constexpr std::string_view func_sig = __FUNCSIG__;
        // MSVC格式示例:"class std::basic_string_view<char,struct std::char_traits<char> > __cdecl Foo<int,int (__cdecl*)(int)>::printFnName(void)"
        constexpr auto sig_end = func_sig.find("::printFnName") - 1;
        constexpr auto name_start = func_sig.rfind(',', sig_end) + 2;
        return func_sig.substr(name_start, sig_end - name_start);
#endif
    }
};

// 测试代码
int bar(int) { return 0; }
#include <iostream>
#include <cstdlib> // for std::abs

int main()
{
    std::cout << Foo<int, bar>::printFnName() << '\n';
    std::cout << Foo<float, std::abs>::printFnName() << '\n';
    return 0;
}

说明:

  • GCC/Clang下输出:bar、std::abs
  • MSVC下输出:bar、std::abs
  • 若需要完整函数签名,可调整字符串解析逻辑,提取__PRETTY_FUNCTION__或__FUNCSIG__中的完整类型描述部分。

运行时方案(跨平台,依赖系统API)

如果允许运行时求值,可通过平台原生API获取函数指针对应的符号名,兼容性更稳定:

#include <string>
#include <iostream>

#ifdef __linux__ || __APPLE__
#include <dlfcn.h>
#include <cxxabi.h> // 用于符号解混淆
#elif _WIN32
#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
#endif

template <class T, T (*Fn)(T)>
struct Foo
{
    static std::string printFnName()
    {
#ifdef __linux__ || __APPLE__
        Dl_info info;
        if (dladdr(reinterpret_cast<void*>(Fn), &info) && info.dli_sname)
        {
            // 解混淆编译器修饰后的符号名
            char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr);
            std::string name(demangled ? demangled : info.dli_sname);
            free(demangled);
            return name;
        }
#elif _WIN32
        char buffer[256];
        DWORD64 addr = reinterpret_cast<DWORD64>(Fn);
        if (SymInitialize(GetCurrentProcess(), nullptr, TRUE))
        {
            SYMBOL_INFO* sym_info = reinterpret_cast<SYMBOL_INFO*>(buffer);
            sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
            sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
            if (SymFromAddr(GetCurrentProcess(), addr, nullptr, sym_info))
            {
                char demangled[256];
                UnDecorateSymbolName(sym_info->Name, demangled, sizeof(demangled), UNDNAME_COMPLETE);
                SymCleanup(GetCurrentProcess());
                return demangled;
            }
            SymCleanup(GetCurrentProcess());
        }
#endif
        return "unknown";
    }
};

// 测试代码
int bar(int) { return 0; }
#include <cstdlib>

int main()
{
    std::cout << Foo<int, bar>::printFnName() << '\n';
    std::cout << Foo<float, std::abs>::printFnName() << '\n';
    return 0;
}

说明:

  • Linux/macOS需链接-ldl编译选项,Windows自动链接dbghelp.lib
  • 代码内置了符号解混淆逻辑,可将编译器生成的修饰名(如MSVC的?bar@@YAHH@Z)转换为可读的函数名

关于C++反射的未来

当前C20及C23的反射特性(如std::reflect相关技术规范)仍处于完善阶段,完整的反射能力预计会在后续标准中正式纳入,届时可以原生地获取函数指针对应的元信息(名称、签名、参数类型等),无需依赖编译器扩展或平台API。

内容的提问来源于stack exchange,提问作者PluginPenguin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:10:41