Как разобрать std :: string как std :: string

Я уже некоторое время использую некоторый код для разборки, чтобы помочь с некоторой отладкой без необходимости писать тысячи строк с динамическими приведениями или реализовывать виртуальные функции, которые возвращают имя класса.

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’, он больше не выводит ‘std :: string’, а вместо этого выводит:

std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >

Конечно, это правильно, так как std :: string является typedef для std :: basic_string.
Хотя, насколько я вижу, в обоих libc ++ как в libstdc ++ это определено одинаково, используя typedef. Так что я не совсем понимаю, почему этот расклад изменился после перехода на libc ++.

Кто-нибудь знает, почему это отличается и как получить ‘std :: string’, если CLASS будет ‘std :: string’, и ‘myTemplate’, когда CLASS будет ‘myTemplate’?

Tnx заранее!

JVApen

2

Решение

libc ++ использует inline namespaces чтобы версия его ABI. Используемое в настоящее время встроенное пространство имен: std::__1, Это было сделано для того, чтобы Apple могла поставлять gcc libstdc ++ и libc ++ одновременно. Dylib A может ссылаться на libstdc ++, а dylib B может ссылаться на libc ++, а приложение может ссылаться на оба dylibs. Когда это происходит, вы не хотите случайно получить libstdc ++ std::string перепутал с libc ++ std::string,

У них один и тот же API, так что было бы легко сделать это случайно, передав std::string через границу дизлиба. Решение состоит в том, чтобы сказать компилятору манипулировать ими по-разному, и это именно то, что делает встроенное пространство имен (и было изобретено для него). Теперь, если они случайно смешиваются в приложении, возникает ошибка времени ссылки, потому что компоновщик видит два разных типа, о чем свидетельствуют их разные искаженные имена.

Работа деманглера заключается в том, чтобы просто сказать вам правду: как называется расколотый символ символа, которым вы его кормите. Работает отлично.

Существует способ отключить управление версиями ABI в libc ++. Поиск <__config> для _LIBCPP_BEGIN_NAMESPACE_STD а также _LIBCPP_END_NAMESPACE_STD, Вы можете увидеть, как некоторые платформы определяют это, чтобы открыть встроенное пространство имен, а некоторые нет. Это очень большой молоток для решения вашей проблемы. Все который компилируется и ссылается на libc ++ на вашей платформе, должен быть перестроен, если вы измените ABI libc ++ таким способом.

Вот более простое частичное решение вашей проблемы, которое я иногда использую:

#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 имеет только несколько «сжатий», которые соответствуют typedefs, подобным этому. Они есть std::string, std::istream, std::ostream а также std::iostream,

7

Другие решения


По вопросам рекламы [email protected]