我一直在使用一些demangling代码来帮助进行一些调试,而不必使用动态强制转换编写数千行,或者必须实现返回类名的虚函数。
template <class CLASS>
std::string getClassName(CLASS &theObject)
{
int status = 0;
// Convert real name to readable string
char *realName = abi::__cxa_demangle(typeid(theObject).name(), nullptr,
nullptr, &status);
ASSERT(status == 0); // Assert for success
VERIFY(realName, return std::string());
// Return as string + prevent memory leaks!
const std::string result(realName);
free(realName);
return result;
}
这段代码背后的想法很简单,输出我们实际使用的类。 虽然在切换到Ubuntu 14.04之后,我再也无法使用clang和c ++ - 11 / c ++ - 14标准进行编译,所以我已经转而使用libc ++而不是libstdc ++。
切换到libc ++之后,我注意到当我解组时,std :: string&#39;它不再输出&#st; :: string&#39;,而是输出:
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
当然这是正确的,因为std :: string是std :: basic_string的typedef。 虽然我在libc ++中都可以看到libstdc ++,但是使用typedef以相同的方式定义。所以我真的不明白为什么通过切换到libc ++来改变这个demangling。
有人知道为什么会有这种不同以及如何获得&#39; std :: string&#39;如果CLASS是&#39; std :: string&#39;,&#39; myTemplate&#39;什么时候CLASS会成为myTemplate&#39;?
提前Tnx!JVApen
答案 0 :(得分:7)
libc ++使用inline namespaces
对其ABI进行版本控制。它当前使用的内联命名空间是std::__1
。这样做是为了让Apple可以同时发布gcc libstdc ++和libc ++。 Dylib A可能链接到libstdc ++,而dylib B可能链接到libc ++,并且应用程序可能链接到两个dylib。当发生这种情况时,您不希望意外地将libstdc ++ std::string
与libc ++ std::string
混淆。
它们具有相同的API,因此通过将std::string
传递到dylib边界会很容易意外。解决方案是告诉编译器以不同方式对它们进行修改,这正是内联命名空间所做的(并且是为其发明的)。现在,如果它们在应用程序中意外混合,将导致链接时间错误,因为链接器会看到两种不同的类型,这可以通过它们的不同错位名称来证明。
demangler的工作就是简单地说实话:你喂它的符号的解码名称是什么。它工作得很好。
确实存在一种在libc ++中关闭ABI版本的方法。搜索&lt; __ config&gt;适用于_LIBCPP_BEGIN_NAMESPACE_STD
和_LIBCPP_END_NAMESPACE_STD
。您可以看到某些平台如何定义此内容以打开内联命名空间,而有些平台则不会。这是一个非常大的锤子用于你的问题。如果以这种方式更改libc ++的ABI,则必须重建在您的平台上编译和链接libc ++的 Everything 。
以下是我有时使用的问题的简单部分解决方案:
#include <iostream>
#include <type_traits>
#include <memory>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <cxxabi.h>
namespace
{
inline
void
filter(std::string& r, const char* b)
{
const char* e = b;
for (; *e; ++e)
;
const char* pb = "std::__1::";
const int pl = std::strlen(pb);
const char* pe = pb + pl;
while (true)
{
const char* x = std::search(b, e, pb, pe);
r.append(b, x);
if (x == e)
break;
r += "std::";
b = x + pl;
}
}
} // unnamed namespace
template <typename T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
__cxxabiv1::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
std::free
);
std::string r;
if (own)
{
if (std::is_const<TR>::value)
r += "const ";
if (std::is_volatile<TR>::value)
r += "volatile ";
filter(r, own.get());
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
}
else
r = typeid(TR).name();
return r;
}
这只是在将错误名称中的::__1
过滤掉之前过滤掉它。如果您愿意,也可以使用相同的技术将std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
转换为std::string
。
Itanium ABI只有少数“压缩”对应于这样的typedef。它们是std::string
,std::istream
,std::ostream
和std::iostream
。