4

私は、動的キャストを使用して何千行も記述したり、クラス名を返す仮想関数を実装したりすることなく、デバッグを支援するために、すでにいくつかのデマングリング コードを使用してきました。

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 標準を使用してコンパイルできなくなったため、libstdc++ の代わりに libc++ を使用するように切り替えました。

libc++ に切り替えた後、「std::string」をデマングルすると、「std::string」が出力されなくなりましたが、代わりに次のように出力されることに気付きました。

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

もちろん、std::string は std::basic_string の typedef であるため、これは正しいことです。libc++ と libstdc++ の両方で見ることができる限り、これは typedef を使用して同じ方法で定義されています。したがって、libc ++に切り替えることでこのデマングリングが変更された理由がよくわかりません。

これが異なる理由と、CLASS が「std::string」の場合は「std::string」、CLASS が「myTemplate」の場合は「myTemplate」を取得する方法を知っている人はいますか?

事前にTnx!

JVAペン

4

1 に答える 1

11

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::stringdylib 境界を越えてしまう可能性があります。解決策は、コンパイラにそれらを別の方法でマングルするように指示することです。これはまさにインライン名前空間が行うことです (そしてそのために発明されました)。これらが誤ってアプリケーション内で混在すると、リンカが 2 つの異なる型を認識するため、リンク時エラーが発生します。

デマングラーの仕事は、単に真実を伝えることです: あなたがフィードしたシンボルのデマングリングされた名前は何ですか? それは完全に機能しています。

libc++ で ABI のバージョン管理をオフにする方法はあります。_LIBCPP_BEGIN_NAMESPACE_STD<__config> でとを検索し_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には、このような typedef に対応する「圧縮」はほんの一握りしかありません。それらはstd::stringstd::istreamstd::ostreamおよびstd::iostreamです。

于 2014-08-03T16:17:17.677 に答える