std::type_info::name の結果を分解する
-
07-07-2019 - |
質問
私は現在、呼び出し元の関数に関する情報を出力することを目的としたログ コードに取り組んでいます。これは比較的簡単なはずです。標準の C++ には type_info
クラス。これには、typeid されたクラス/関数などの名前が含まれます。しかし、それはめちゃくちゃです。あまり役に立ちません。つまり、 typeid(std::vector<int>).name()
戻り値 St6vectorIiSaIiEE
.
これから何か役に立つものを生み出す方法はあるでしょうか?のように std::vector<int>
上の例の場合。テンプレート以外のクラスでのみ機能する場合でも問題ありません。
この解決策は gcc でも機能するはずですが、移植できればもっと良いでしょう。これはログ記録用なので、オフにできないほど重要ではありませんが、デバッグには役立つはずです。
解決
この質問/回答が注目を集め、皆様からの貴重なフィードバックを踏まえると、 GマンニックG, コードを少し整理しました。次の 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()
成功しています、 スタックに割り当てられたローカル配列へのポインタを返します。...痛い!
バッファを提供する場合は、 abi::__cxa_demangle()
ヒープ上に割り当てられることを前提としています。スタック上にバッファを割り当てるのはバグです (GNU ドキュメントより)。 "もし output_buffer
長さが足りないため、次を使用して展開されます realloc
." 電話をかける realloc()
スタックへのポインタ上...痛い!(こちらも参照 イーゴリ・スコチンスキーさんの優しいコメントです。)
これらのバグは両方とも簡単に確認できます。受け入れられた回答(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;
}
他のヒント
ブーストコアにはデマングラーが含まれています。チェックアウト 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
の単なるラッパーです。
これは私たちが使用するものです。 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
ここで、 type_strings.hpp をご覧ください。必要なことを行う関数が含まれています。
デマングルツールを探している場合、ログファイルに表示されるものを破壊するために使用できます。binutilsに付属するc++filt
を見てください。 C ++およびJavaのシンボル名を解読できます。
完全な解決策ではありませんが、いくつかの標準(または広くサポートされている)マクロの定義を調べてください。マクロの使用を確認するのはコードのロギングで一般的です:
__FUNCTION__
__FILE__
__LINE__
e.g.:
log(__FILE__, __LINE__, __FUNCTION__, mymessage);
実装は定義されているため、移植性のあるものではありません。 MSVC ++では、name()は装飾されていない名前であり、raw_name()を見て装飾された名前を取得する必要があります。
ここでは暗闇の中で突き刺しますが、gccの下では、を見ることができます。 demangle.h
また、__PRETTY_FUNCTION__
というマクロを見つけました。それはきれいな関数名を与えます(図:))。これが私が必要としたものです。
つまり次のようになります:
virtual bool mutex::do_unlock()
しかし、他のコンパイラでは動作しないと思います。
アリのソリューションのわずかなバリエーション。それでもコードを非常によくしたい場合は
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のソリューションと同じままです
__cxa_demangle
にあるcxxabi.h
を見てください。
// 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;
}
承認された解決策 [1]はほとんどうまくいきます。 少なくとも1つのケースを見つけました(そして、私はそれをコーナーケースとは呼びません)。
これらのケースについては、下部に投稿された別のソリューションを見つけました。
問題のあるケース([1]で定義されているtype
を使用):
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;
}
type_infoを常に使用したかったのですが、name()メンバー関数の結果は非標準であり、意味のある結果に変換できるものを必ずしも返すとは限りません。
1つのコンパイラーに固執している場合は、おそらく、コンパイラー固有の機能が必要なことを行います。ドキュメントを確認してください。