116

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

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

解決策は gcc で機能するはずですが、移植できればもっと良いでしょう。ロギング用なのでオフにできないほど重要ではありませんが、デバッグには役立つはずです。

4

14 に答える 14

136

この質問/回答が注目され、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

g++ 4.7.2、g++ 4.9.0 20140302 (実験的)、clang++ 3.4 (トランク 184647)、clang 3.5 (トランク 202594)、Linux 64 ビットおよび 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()が成功すると、スタックに割り当てられたローカル配列へのポインターが返されます
また、バッファを提供する場合はabi::__cxa_demangle()、ヒープに割り当てられると想定することに注意してください。スタックにバッファを割り当てるのはバグです (gnu doc から): "If output_bufferis not long, it is expand using realloc." スタックへのポインタを呼び出しrealloc()ています... ああ!( Igor Skochinskyの親切なコメントも参照してください。)

これらのバグの両方を簡単に確認できます。受け入れられた回答 (2013 年 9 月 7 日現在) のバッファー サイズを 1024 から 16 などの小さいサイズに減らし、 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;
}
于 2010-12-27T20:10:34.597 に答える
36

ブーストコアにはデマングラーが含まれています。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以前に提案されたように、これは基本的に単なるラッパーです。

于 2016-01-21T06:31:40.333 に答える
11

これが私たちが使用するものです。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  
于 2008-11-11T19:21:39.660 に答える
10

ここで、必要なことを行う関数が含まれているtype_strings.hppを見てください。

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

于 2008-11-11T19:31:18.577 に答える
5

これは実装定義なので、移植できるものではありません。MSVC++ では、name() は装飾されていない名前であり、装飾された名前を取得するには raw_name() を確認する必要があります。ここでは暗闇の中で突き刺すだけですが、gcc の下では、 demangle.h
を見たいと思うかもしれません。

于 2008-11-11T18:59:52.230 に答える
4

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

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

virtual bool mutex::do_unlock()

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

于 2008-11-11T20:14:36.103 に答える
3

受け入れられている解決策[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;
}
于 2018-12-20T09:26:23.927 に答える
2

完全な解決策ではありませんが、標準の(または広くサポートされている)マクロの定義を確認することをお勧めします。ロギングコードでは、マクロの使用を確認するのが一般的です。

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
于 2008-11-11T19:23:21.730 に答える
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は、アリのソリューションと同じままです

于 2015-04-13T13:38:56.823 に答える
1

__cxa_demangleで見つけることができるものを見てくださいcxxabi.h

于 2008-11-11T19:13:36.493 に答える
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;
  }
于 2010-04-12T18:57:48.143 に答える
0

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

于 2008-11-11T19:02:31.337 に答える