【问题标题】:get a c++ function mangled name at compile time (or runtime)在编译时(或运行时)获取一个 c++ 函数重整名称
【发布时间】:2017-04-09 23:03:57
【问题描述】:

我有一个函数类方法,ValueHolder::printValue

class ValueHolder {

public:
    void printValue ();
} ;

如何在编译时(或运行时)确定它的重命名。

例如,我想这样做:

const char *mangled_name = GetMangledNameOfSymbol(&ValueHolder::printValue);

此函数可能会返回如下字符串:

"_ZN11ValueHolder10printValueEv"

根据@Marco A。先决条件是现代编译器。支持 typeid 并打开标志以启用此功能的一种。

我还将接受一个适用于 Gcc 和 Clang 的实用答案,以及一个适用于 MSVC 的存根。

【问题讨论】:

    标签: c++ name-mangling


    【解决方案1】:

    根据[lib.type.info],没有标准的方法来做到这一点

    类 type_info 描述了实现生成的类型信息。此类的对象有效地存储了指向类型名称的指针,以及适合比较两种类型的相等性或整理顺序的编码值。类型的名称、编码规则和排序顺序都是未指定的,并且可能因程序而异。

    对于您的编译器实现,您可以使用typeid(type/expression).name(),但没有指定或强制该名称将被修饰(它是实现定义的)。它还取决于使用的编译标志(感谢 malat)。

    例子:

    class ValueHolder {
    
    public:
      void printValue();
    };
    
    
    int main() {
      std::cout << typeid(&ValueHolder::printValue).name();
    }
    

    gcc7.0

    M11ValueHolderFvvE

    clang4.0

    M11ValueHolderFvvE

    MSVC14

    void (__cdecl ValueHolder::*)(void) __ptr64

    【讨论】:

    • 还请澄清它不仅取决于编译器 + 版本,还取决于编译标志(例如 std=c++11 和 std::string,和/或类似 -DGLIBCXX_DEBUG 的东西)
    • @malat 谢谢,我会把那篇文章添加到答案中。
    • 这是一个很好的答案。我将稍微改变一下这个问题,以反映使用现代编译器。
    • @MarcoA。好的,修改了问题。 :::: 我认为,虽然理论上你是正确的,但实际上还是有可能的。在 Clang 和 Gcc 下,我可以获得命名空间+类的错误名称,并且可以获得描述函数参数的错误名称。我可以使用预处理器向导,确定函数名称,然后将它们连接在一起。
    【解决方案2】:

    我会添加一个答案,但我不会将其标记为正确。它不完整。太大,无法添加为评论。这是我可以做的事情,但我正在寻找更好的方法。而且,是的,非常俗气。但我认为某处有一些 API,虽然仍然有点粗俗,但可以保证工作(如果在整个项目中使用单个编译器)。

    template<typename R, typename C, typename... A>
    struct MemberFunctionPointer
    {
        typedef R Return;
        typedef C Class;
    };
    
    template<typename R, typename C, typename... A>
    constexpr auto inferMemberFunctionPointer(R (C::*method)(A...))
    {
        return MemberFunctionPointer<R,C,A...>{};
    }
    
    template<typename M, M m, typename... A>
    class GenerateMethodSignature
    {
        typedef typename decltype(inferMemberFunctionPointer(m))::Class T;
        typedef typename decltype(inferMemberFunctionPointer(m))::Return R;
    
    
    public:
        static const char *mangledName (const char *fs)
        {
            const char *ts = typeid(T).name();
            const char *rs = typeid(R).name();
            const char *ms = typeid(M).name();
    
            std::string r = "_Z";
            if (ts[0] != 'N')
                r += "N";
            r += ts;
            if (ts[0] == 'N')
                r.pop_back();
    
            r += std::to_string(strlen(fs));
            r += fs;
            r += "E";
    
            r += ms + strlen ("M") + strlen(ts) + strlen ("F") + strlen(rs);
            r.pop_back();
    
            printf("calculated signature %s\n", r.c_str());
    
            // this is very bad but... for demonstration purposes
            return strdup(r.c_str());
        }
    } ;
    
    namespace MyNamespace {
    namespace MySubNamespace {
    class MyClass
    {
    public:
        int MyFunction (int myarg);
    } ;
    } // namespace
    } // namespace
    
    #define ExportSignature(T, M) GenerateMethodSignature<decltype(&T::M), &T::M>::mangledName(#M)
    const char *myMethodSignature = ExportSignature(MyNamespace::MySubNamespace::MyClass, MyFunction);
    

    【讨论】:

    • 看起来很有希望。但似乎在所有情况下都无法正常工作,例如尝试将 MyFunction 的返回类型更改为 std::string。
    • 在这种情况下,将 B5cxx11 后缀添加到函数名称中。另外,如果你还把参数类型改成std::string,输出就完蛋了……
    • 是的,这个答案并不完整,只是一个演示。我希望有人会有更好的方法,而不是 hacky。
    • 好吧,搜索了很多之后,我放弃了。当参数使用与返回值相同的类型时,可以手动应用“压缩规则”来解压缩损坏的名称,但我不确定是否可以轻松应用 ABI 标签......无论如何,供我使用在这种情况下,我想匹配两个重整的名称(其中一个是我构建的,另一个来自目标文件),我决定反过来构建规范的重整名称并比较它们。快速浏览一下,Boost.DLL 似乎使用了类似的方法:它不是创建重整的名称,而是对所有符号进行解包以查找...
    【解决方案3】:

    在windows平台运行时理论上可以使用dbghelp.h/lib

    winapi get the mangled name from a function's address

    DWORD options = SymGetOptions();
    SymSetOptions(options & ~SYMOPT_UNDNAME);
    if (SymFromAddr(hProcess, dwAddress, &dwDisplacement, pSymbol))
    {
        // etc...
    }
    SymSetOptions(options);
    

    这将在运行时解析损坏的函数名称。 但必须导出符号(使用__declspec(dllexport)

    【讨论】:

      【解决方案4】:

      您可以做的是使用 g++ 编译您的 C++ 程序并获取 .o 文件。对这样获得的 .o 文件运行“nm”命令以获取损坏的名称!这种方法在 Linux 系统上是可行的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-04
        • 2014-07-19
        • 2011-11-20
        • 2022-11-28
        • 1970-01-01
        • 2018-01-17
        • 2018-11-07
        相关资源
        最近更新 更多