get a c++ function mangled name at compile time (or runtime)
Asked Answered
F

4

24

I have a function class method, ValueHolder::printValue

class ValueHolder {

public:
    void printValue ();
} ;

How do I determine its mangled name at compile time (or runtime).

For instance I would like to do this:

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

This function might return a string like:

"_ZN11ValueHolder10printValueEv"

As per @Marco A. A prerequisite is a modern compiler. One that supports typeid, and with flags turned on to enable this feature.

I will also accept an answer which can work in practicality for Gcc & Clang, and a stub for MSVC.

Flossi answered 25/11, 2016 at 14:4 Comment(0)
D
12

There's no standard way of doing this, according to [lib.type.info]

The class type_info describes type information generated by the implementation. Objects of this class effectively store a pointer to a name for the type, and an encoded value suitable for comparing two types for equality or collating order. The names, encoding rule, and collating sequence for types are all unspecified and may differ between programs.

and to your compiler implementation you could use typeid(type/expression).name() but it is nowhere specified or enforced that this name will be decorated (it is implementation-defined). It also depends on the compilation flags used (thanks malat).

Example:

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

Dough answered 25/11, 2016 at 14:13 Comment(4)
Please also clarify that it not only depends on a compiler + version, but also compilation flags (eg std=c++11 and std::string, and/or stuff like -DGLIBCXX_DEBUG)Auricular
@Auricular Thank you, I'll add that piece to the answer.Dough
This is a great answer. I'm going to change the question just a tad, to reflect using a modern compiler.Flossi
@MarcoA. Ok, modified the question. :::: I think, although in theory you are correct, but in practice it is still possible. Under Clang and Gcc I can get a mangled name for the namespace+class and I can get a mangled name describing the function arguments. I can with preprocessor wizarding, determine the function name and then concat them all together.Flossi
F
5

I'll add an answer, but I'm not going to mark it correct. It is not complete. Too big to add as a comment. This is something along the lines which I can do, but I'm looking for a better way. And, yes, very tacky-hacky. But I figure there is some API somewhere which, although still will be a bit gross, will be guaranteed to work (if using a single compiler throughout a project).

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);
Flossi answered 25/11, 2016 at 15:26 Comment(5)
Looks promising. But seems to not work correctly in all cases, e.g. try changing the return type of MyFunction to std::string.Teodor
In which case, a B5cxx11 postfix is added to the function name. Also, if you also change the parameter type to std::string, the output is completely screwed...Teodor
Yeah, this answer is not complete, just a demonstration. I was hoping someone would have a better way which was not as hacky.Flossi
Well, after searching a lot, I gave up. It is possible to manually apply 'compression rules' to de-compress mangled name when a parameter uses the same type as the return value, but I'm not sure if the ABI tag can be applied easily... Anyway, for my use case, in which I wanted to match two mangled names (one of which I construct, the other from an object file), I decided to go the other way around and build canonical demangled names and compare them. With a quick look, it seems that Boost.DLL has use a similar method: instead of creating mangled name, it demangles all symbols to find ...Teodor
I would say this is a great answer. It just needs some fixing up. For one, instead of strdup which is deprecated; why not just return the same std::string we were building? Secondly, it only supports member functions; support for regular functions can be added. Then the proper prefix and suffixes need to be updated. For one, const this pointer requires a K prefix after the N prefix. Furthermore, there is no inferMemberFunction for pointer to member function const. Then last but maybe not final, is a flag parameter to tell the platform to target. As Windows requires extra underscrore prefix.Carmon
F
2

Well what you can do is compile your C++ program using g++ and get the .o file. Run the 'nm' command on the .o file thus obtained to get the mangled names! This method is viable on Linux systems.

Fourcycle answered 25/11, 2016 at 14:8 Comment(1)
I don't know why it got downvoted. it is a legit idea. it is a start, it is not complete. You will need to demangle symbols you extracted this way, and generate .h file with a map from &symbol --> mangled name. You can do that by demangaling the symbols found and extracting the c++ identifiers from them. This will work modulus template specializations and functions override. These cases require deeper parsing of the source code.Glynis
C
1

At the runtime for windows platform in theory it's possible to use 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);

This would resolve mangled function name at runtime. But the symbol MUST be exported (using __declspec(dllexport))

Contaminant answered 6/11, 2021 at 7:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.