Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 228
iamacomputer Avatar asked Nov 25 '16 14:11

iamacomputer


People also ask

How do you find the mangled function?

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.

How do you stop name mangling in C++?

To prevent the C++ compiler from mangling the name of a function, you can apply the extern "C" linkage specifier to the declaration or declarations, as shown in the following example: extern "C" { int f1(int); int f2(int); int f3(int); };

Why does C++ name mangle?

Name mangling is the encoding of function and variable names into unique names so that linkers can separate common names in the language. Type names may also be mangled. Name mangling is commonly used to facilitate the overloading feature and visibility within different scopes.

How do you know if your name is mangling?

linux gnu tool chain nm command can be used to see mangled name.


2 Answers

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

like image 148
Marco A. Avatar answered Nov 13 '22 13:11

Marco A.


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);
like image 38
iamacomputer Avatar answered Nov 13 '22 12:11

iamacomputer