patterncppMinor
Poor Man's UnDecorateSymbolName
Viewed 0 times
manundecoratesymbolnamepoor
Problem
Following is my hand-rolled implementation of name demangling for names generated by Microsoft's compiler, partially reimplementing the
```
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 == '_
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:
-
Take the mangled name as a
-
Use the standard algorithms. They will the intent of your loops much clearer. For instance, you have this loop:
Which can become:
Or, for a longer example, building up your names:
The one-liners are easier to understand than the loops.
-
Use enumerations in a switch. Where you have:
Prefer instead to write:
Expressed intent more clearly.
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') // DestructorPrefer instead to write:
switch(funcName[1]) {
case Mangled::CONSTRUCTOR:
...
case Mangled::DESTRUCTOR:
...Expressed intent more clearly.
- In
getCallConv()andgetTypeName(), both makes can bestd::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') // DestructorContext
StackExchange Code Review Q#110620, answer score: 3
Revisions (0)
No revisions yet.