HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Poor Man's UnDecorateSymbolName

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
manundecoratesymbolnamepoor

Problem

Following is my hand-rolled implementation of name demangling for names generated by Microsoft's compiler, partially reimplementing the UnDecorateSymbolName WinAPI function.

```
static std::string getCallConv(char code)
{
static const std::map callConvs{
{ 'A', "__cdecl " },
{ 'I', "__fastcall" },
{ 'E', "__thiscall" },
{ 'G', "__stdcall " }
};
auto iter = callConvs.find(code);
return (iter != std::end(callConvs)) ? iter->second : "";
}

static std::string getTypeName(char code)
{
static const std::map types{
{ 'C', "signed char " },
{ 'D', "char " },
{ 'E', "unsigned char " },
{ 'F', "short " },
{ 'G', "unsigned short" },
{ 'H', "int " },
{ 'I', "unsigned int " },
{ 'J', "long " },
{ 'K', "unsigned long " },
{ 'M', "float " },
{ 'N', "double " },
{ 'O', "long double " },
// These are just placeholders. A better demangler
// would replace them with the actual type names.
{ 'P', "void* " },
{ 'Q', "void[] " },
{ 'U', "struct* " },
{ 'V', "class* " },
{ 'X', "void " },
{ 'Z', "... " }
};
auto iter = types.find(code);
return (iter != std::end(types)) ? iter->second : "";
}

std::string demangle(const char * mangledName, bool baseNameOnly)
{
std::string demangledName;
const char * ptr = mangledName;

if (ptr == nullptr || *ptr == '\0')
{
return demangledName;
}

// MSFT C++ names always start with a question mark.
if (*ptr != '?')
{
// Assume a C function with the default underscore prefix,
// returning the original name minus the underscore. It might
// also contain more name decoration at the end, so ignore
// anything after the first '@' character.
if (*ptr == '_

Solution

-
Your function is too long. It's ~200 lines long, and is already logically made up of disparate parts. Break it up into smaller chunks. For instance:

if (*ptr != '?') {
    return demangle_c_function(mangledName);
}


-
Take the mangled name as a std::string const&. We're really not concerned about performance here, and it'll make many parts of the code easier to write.

-
Use the standard algorithms. They will the intent of your loops much clearer. For instance, you have this loop:

for (++ptr; *ptr != '\0' && *ptr != '@'; ++ptr)
{
    demangledName.push_back(*ptr);
}


Which can become:

auto start = mangledName.begin() + 1;
auto end = std::find(start, mangledName.end(), '@');
demangledName = std::string(start, end);


Or, for a longer example, building up your names:

auto it = mangledName.begin() + 1;
auto func_end = std::find(it, mangledName.end(), '@');
std::string funcName(it, func_end);
it = func_end;

if (it != mangledName.end()) {
    auto class_end = std::find(it+1, mangledName.end(), '@');
    std::string className(it+1, class_end);
    it = std::find_if(it, mangledName.end(), [](char c){ c != '@';});

    // do something with className in a different function
}


The one-liners are easier to understand than the loops.

-
Use enumerations in a switch. Where you have:

if (funcName[1] == '0') // Constructor
{
    ...
}
else if (funcName[1] == '1') // Destructor


Prefer instead to write:

switch(funcName[1]) {
case Mangled::CONSTRUCTOR:
    ...
case Mangled::DESTRUCTOR:
    ...


Expressed intent more clearly.

  • In getCallConv() and getTypeName(), both makes can be std::unordered_map.

Code Snippets

if (*ptr != '?') {
    return demangle_c_function(mangledName);
}
for (++ptr; *ptr != '\0' && *ptr != '@'; ++ptr)
{
    demangledName.push_back(*ptr);
}
auto start = mangledName.begin() + 1;
auto end = std::find(start, mangledName.end(), '@');
demangledName = std::string(start, end);
auto it = mangledName.begin() + 1;
auto func_end = std::find(it, mangledName.end(), '@');
std::string funcName(it, func_end);
it = func_end;

if (it != mangledName.end()) {
    auto class_end = std::find(it+1, mangledName.end(), '@');
    std::string className(it+1, class_end);
    it = std::find_if(it, mangledName.end(), [](char c){ c != '@';});

    // do something with className in a different function
}
if (funcName[1] == '0') // Constructor
{
    ...
}
else if (funcName[1] == '1') // Destructor

Context

StackExchange Code Review Q#110620, answer score: 3

Revisions (0)

No revisions yet.