std :: type_info :: nameの結果の展開


93

私は現在、とりわけ、呼び出し元の関数に関する情報を出力することになっているいくつかのロギングコードに取り組んでいます。これは比較的簡単なはずtype_infoです。標準のC ++にはクラスがあります。これには、typeidされたクラス/関数/などの名前が含まれます。しかし、それは壊れています。あまり役に立ちません。すなわちtypeid(std::vector<int>).name()戻りますSt6vectorIiSaIiEE

これから何か有用なものを生み出す方法はありますか?同様にstd::vector<int>、上記の例のために。テンプレート以外のクラスでのみ機能する場合も問題ありません。

ソリューションはgccで機能するはずですが、移植できればより良いでしょう。これはロギング用であるため、オフにできないことはそれほど重要ではありませんが、デバッグには役立ちます。

回答:


117

この質問/回答が受け取る注意とGManNickGからの貴重なフィードバックを考慮して、コードを少しクリーンアップしました。2つのバージョンが提供されます。1つはC ++ 11機能を備え、もう1つはC ++ 98機能のみを備えています。

type.hppファイル

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

template <class T>
std::string type(const T& t) {

    return demangle(typeid(t).name());
}

#endif

type.cppファイル(C ++ 11が必要)

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    return (status==0) ? res.get() : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif

使用法:

#include <iostream>
#include "type.hpp"

struct Base { virtual ~Base() {} };

struct Derived : public Base { };

int main() {

    Base* ptr_base = new Derived(); // Please use smart pointers in YOUR code!

    std::cout << "Type of ptr_base: " << type(ptr_base) << std::endl;

    std::cout << "Type of pointee: " << type(*ptr_base) << std::endl;

    delete ptr_base;
}

それは印刷します:

ptr_baseのBase*
タイプ:指示先のタイプ:Derived

Linux 64ビット上のg ++​​ 4.7.2、g ++ 4.9.0 20140302(実験的)、clang ++ 3.4(トランク184647)、clang 3.5(トランク202594)およびg ++ 4.7.2(Mingw32、Win32 XP SP2)でテスト済み。

C ++ 11機能を使用できない場合、C ++ 98でこれを行う方法を次に示します。ファイルtype.cppは次のようになります。

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

struct handle {
    char* p;
    handle(char* ptr) : p(ptr) { }
    ~handle() { std::free(p); }
};

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );

    return (status==0) ? result.p : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif


(2013年9月8日更新)

受け入れられた回答(2013年9月7日現在)への呼び出しabi::__cxa_demangle()が成功すると、ローカルのスタック割り当て配列へのポインタが返されます ... ouch!
また、バッファーを提供する場合abi::__cxa_demangle()は、ヒープ上に割り当てられていると想定します。スタックにバッファを割り当てることはバグです(GNUドキュメントから):output_buffer十分に長くない場合、それはを使用して拡張されreallocます。」 スタックへのポインタを呼び出すrealloc() ...痛い!(Igor Skochinskyの親切なコメントも参照してください。)

単なる例16のために、より小さなものに1024年から(2013年9月7日の時点で)受け入れ答えにバッファサイズを小さくし、名前でそれに何かを与える:あなたは簡単にこれらのバグの両方を確認することができないので、(長い15以上realloc()であります呼び出されません)。それでも、システムとコンパイラの最適化に応じて、出力は次のようになります。
2番目のバグを確認するには:バッファーサイズを1に設定し、名前が1文字より長いもので呼び出します。実行すると、プログラムrealloc()はスタックへのポインタを使用して呼び出しを試みるため、ほぼ確実にクラッシュします。


(2010年12月27日の古い回答)

KeithBのコードに加えられた重要な変更:バッファーは、mallocによって割り当てられるか、NULLとして指定される必要があります。スタックに割り当てないでください。

そのステータスもチェックするのが賢明です。

見つけられませんでしたHAVE_CXA_DEMANGLE。私はチェック__GNUG__しますが、コードがコンパイルされることさえ保証していません。誰かがより良いアイデアを持っていますか?

#include <cxxabi.h>

const string demangle(const char* name) {

    int status = -4;

    char* res = abi::__cxa_demangle(name, NULL, NULL, &status);

    const char* const demangled_name = (status==0)?res:name;

    string ret_val(demangled_name);

    free(res);

    return ret_val;
}

これには必要なことに注意してください#include <cxxabi.h>。それ以外の場合は、うまくいきました。
jterrace

2
docsから:output_bufferデマングルされた名前が格納される、mlengthで割り当てられた* lengthバイトのメモリ領域。output_bufferが十分に長くない場合は、reallocを使用して拡張されます。代わりに、output_bufferをNULLにすることができます。その場合、デマングルされた名前は、mallocで割り当てられたメモリ領域に配置されます。
Igor Skochinsky 2012

2
@IgorSkochinskyはい、以前のコメントにタイプミスがありますが、編集できません。私が書きたかったこと:「前回チェックしたところ、ヒープにabi::__cxa_demangle割り当てられると予想されていました」ドキュメントを検索していただき、ありがとうございます。
アリ

1
技術的にret_valは、構築中にスローするとリークする可能性があることに注意してください。スコープガードを使用して、それを防ぐことができます。
GManNickG 2013

3
std::unique_ptr<char, decltype(&std::free)>ポインターのシグニチャーとして使用するほうがより明確である場合。
mindvirus 2014

27

ブーストコアにはデマングラが含まれています。core / demangle.hppをチェックアウトします

#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>

template<class T> struct X
{
};

int main()
{
    char const * name = typeid( X<int> ).name();

    std::cout << name << std::endl; // prints 1XIiE
    std::cout << boost::core::demangle( name ) << std::endl; // prints X<int>
}

abi::__cxa_demangle以前に提案されたように、これは基本的に単なるのラッパーです。


1
ブーストがオプションの場合、これが最善の方法です。
hbobenicio

13

これが私たちが使用するものです。HAVE_CXA_DEMANGLEは、利用可能な場合にのみ設定されます(GCCの最新バージョンのみ)。

#ifdef HAVE_CXA_DEMANGLE
const char* demangle(const char* name)
{
   char buf[1024];
    unsigned int size=1024;
    int status;
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    return res;
  }
#else
const char* demangle(const char* name)
{
  return name;
}
#endif  

6
含める必要があります#include <cxxabi.h>
fuenfundachtzig 2010年

面白い。HAVE_CXA_DEMANGLEが定義されていない__cxa_demangleがあります
mkb

@Matt私が言ったことは、autoconfに基づくビルドシステムは、HAVE_CXA_DEMANGLEが利用可能な場合にのみそれを設定するということです。
KeithB

19
警告!上記のコードはプログラムをクラッシュさせる可能性があります。バッファーは、mallocによって割り当てられるか、NULLとして指定される必要があります。スタックに割り当てないでください。以下の私のコードを参照してください。
アリ

注意してください、resはNULLを返す可能性があります:)
Zibri 2013年

8

ここでは、必要な機能を実行する関数が含まれているtype_strings.hppを見てください。

たとえば、ログファイルに表示されるものを壊すために使用できるデマングルツールを探している場合c++filtは、binutilsに付属しているを見てください。C ++およびJavaのシンボル名を復号化できます。


ただ注意してください。cxa_demange()(リンクされたコードが使用)とcx ++ filtはどちらもgcc固有です。これを行うポータブルな方法はありません。
KeithB 2008

c ++ filtはそれをカットしません、私はコンパイル時にこれ(またはそのほとんど)を必要としますが、ほとんどはマクロで行われます。
ターミナル

4
type_strings.cppへのリンクが壊れているようです。
StackedCrooked

1
こんにちは@GregoryPakosz上記のコメントのgithubリンクも壊れているようです:( Cheers
olibre

ただ重要な参考情報:abi::__cxa_demangle()とその由来<cxxabi.h> はGCC固有ではありません –遠い過去にはGCCのみであった可能性がありますが、この投稿の執筆時点で<cxxabi.h>は、その場限りの標準でした。解答のコードリンクがDOIであったので、私はクランのから、...クランは、この場合には第一級のサポートを提供するためのQVを保証することができますlibcxxabiソース:それぞれの供述、IMPL、巨大なテスト:git.io/vRTBogit.io/vRTBhをgit.io / vRTRf –テストコードのコメントは、Clangの実装が、GCCと比較して、どういうわけか、よりデマングルできると指摘しています。
fish2000

4

これは実装で定義されているため、移植可能なものではありません。MSVC ++では、name()は装飾されていない名前であり、装飾された名前を取得するにはraw_name()を調べる必要があります。
ここでは暗闇に突き刺さっていますが、gccではdemangle.hを確認することをお勧めします


3

完全なソリューションではありませんが、いくつかの標準(または広くサポートされている)マクロの定義を確認する必要があります。ロギングコードでは、マクロの使用を確認するのが一般的です。

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
言うまでもなくPRETTY_FUNCTION
CesarB 2008

1
これにより、コードのどこにいるかに関する情報が得られます。質問が尋ねたのは、std :: vectorのような型のかなりの名前でした。
KeithB 2008

彼はそれがデバッグ用であると述べました、そして私はそれが完全な解決策ではなかったと述べました。FUNCDNAMEなどの他のマクロは、装飾された名前を返します。
luke

実際、質問を読み直すと、「私は現在、とりわけ、呼び出し元の関数に関する情報を出力することになっているいくつかのロギングコードに取り組んでいます。」これは機能します。
Max Lybbert 2008

名前空間がわからないので、完全ではありません。これはすでに私のコードで使用されています。とにかくありがとう。
ターミナル

3

__PRETTY_FUNCTION__トリックを実行するというマクロも見つけました。それはかなりの関数名を与えます(図:))。これが私が必要としたものです。

つまり、次のようになります。

virtual bool mutex::do_unlock()

しかし、他のコンパイラでは動作しないと思います。


はい、PRETTY_FUNCTIONはgcc固有です。
グレッグロジャース

2

アリのソリューションのわずかなバリエーション。あなたがコードをまだ非常に似ているようにしたい場合

typeid(bla).name()

代わりにこれを書く

Typeid(bla).name() (大文字の最初の文字のみが異なる)

次に、これに興味があるかもしれません:

type.hppファイル

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

/*
template <class T>
std::string type(const T& t) {

  return demangle(typeid(t).name());
}
*/

class Typeid {
 public:

  template <class T>
    Typeid(const T& t) : typ(typeid(t)) {}

  std::string name() { return demangle(typ.name()); }

 private:
  const std::type_info& typ;
};


#endif

type.cppはAliのソリューションと同じままです


1

__cxa_demangle見つけられるものを見てくださいcxxabi.h


私が受け取ったメッセージによると、私はそれを非難しました。
ターミナル

そのメッセージはどこで見つけましたか?私はそれをグーグルでググっただけで、サポートされているようで、廃止されたという証拠はありません。
アリ

多分それは::名前空間では非推奨です。abi :: __ cxa_demangleを使用すると、警告は表示されません。どのgccを使用していますか?
オニタケ

1
// KeithB's solution is good, but has one serious flaw in that unless buf is static
// it'll get trashed from the stack before it is returned in res - and will point who-knows-where
// Here's that problem fixed, but the code is still non-re-entrant and not thread-safe.
// Anyone care to improve it?

#include <cxxabi.h>

// todo: javadoc this properly
const char* demangle(const char* name)
{
    static char buf[1024];
    size_t size = sizeof(buf);
    int status;
    // todo:
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    buf[sizeof(buf) - 1] = 0; // I'd hope __cxa_demangle does this when the name is huge, but just in case.
    return res;
  }

11
警告!バッファーは、mallocによって割り当てられるか、NULLとして指定される必要があります。スタックに割り当てないでください。以下の私のコードを参照してください。
アリ

1

受け入れられた解決策 [1]は、主に適しています。私は少なくとも1つのケースを見つけました(そしてそれをコーナーケースとは呼ばないでしょう)。これは、私が期待したことを報告していない...参照付きです。

それらの場合、私は下部に投稿された別の解決策を見つけました。

問題のあるケースtype[1]で定義されているものを使用):

int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;

作り出す

Type of i is int
Type of ri is int

解決策(を使用type_name<decltype(obj)>()、以下のコードを参照):

cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;

作り出す

Type of i is int
Type of ri is int&

必要に応じて(少なくとも私は)

コード 。特殊化の問題により、個別にコンパイルされたソースではなく、インクルードされたヘッダーにある必要があります。参照テンプレート関数に未定義の参照インスタンスのために。

#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

0

私はいつもtype_infoを使用したいと思っていましたが、name()メンバー関数の結果は非標準であり、意味のある結果に変換できるものを必ずしも返すとは限りません。
1つのコンパイラーを使用している場合は、コンパイラー固有の関数を使用して、必要な処理を行うことができます。ドキュメントを確認してください。


0

Aliのソリューションに続いて、私の使用法に最適なC ++ 11テンプレートの代替案を次に示します。

// type.h
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <typename T>
std::string demangle() {
  int status = -4;

  std::unique_ptr<char, void (*)(void*)> res{
      abi::__cxa_demangle(typeid(T).name(), NULL, NULL, &status), std::free};
  return (status == 0) ? res.get() : typeid(T).name();
}

使用法:

// main.cpp
#include <iostream>

namespace test {
    struct SomeStruct {};
}

int main()
{
    std::cout << demangle<double>() << std::endl;
    std::cout << demangle<const int&>() << std::endl;
    std::cout << demangle<test::SomeStruct>() << std::endl;

    return 0;
}

印刷されます:

double                                                                        
int                                                                           
test::SomeStruct
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.