C++でテンプレート型からchar*名を取得することは可能ですか
質問
テンプレート型の文字列名(const char*)を取得したい。残念ながら、RTTI にアクセスできません。
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return /* magic goes here */; }
}
それで
SomeClass<int> sc;
sc.GetClassName(); // returns "int"
これは可能でしょうか?方法が見つからず、諦めかけています。助けてくれてありがとう。
解決
はありません、それはどちらかのタイプIDを持つ信頼性の高い動作しません。それはあなたのコンパイラの実装に依存していくつかの内部の文字列を与えます。 "INT" のようなものが、また、 "i" はint
のために一般的です。
ところで、何をしたいことは2つのだけ種類が同じであるかどうかを比較するのであれば、あなたは最初の文字列に変換する必要はありません。あなただけ行うことができます。
template<typename A, typename B>
struct is_same { enum { value = false }; };
template<typename A>
struct is_same<A, A> { enum { value = true }; };
そして行う
if(is_same<T, U>::value) { ... }
ブーストはすでに、このようなテンプレートを持っており、次のC ++標準では、あまりにもstd::is_same
ています。
種類の手動登録
あなたはこのようなタイプに特化することができます:
template<typename>
struct to_string {
// optionally, add other information, like the size
// of the string.
static char const* value() { return "unknown"; }
};
#define DEF_TYPE(X) \
template<> struct to_string<X> { \
static char const* value() { return #X; } \
}
DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...
だから、あなたは
のようにそれを使用することができますchar const *s = to_string<T>::value();
あなたはタイプが知られていない場合は、コンパイル時エラーを取得したい場合は、もちろん、あなたはまた、主要なテンプレート定義を取り除く(そして唯一の前方宣言を維持する)ことができます。私はちょうどここに完成のためにそれが含まれています。
私は以前に静的char型のconstのデータ・メンバー*を使用しますが、彼らはそうでそれらの宣言を置く、とする質問のように、いくつかの複雑な問題を引き起こします。上記のようなクラスの専門分野は、簡単に問題を解決します。
自動、GCCに応じて
別のアプローチは、コンパイラの内部に依存することです。 GCCでは、以下は私に合理的な結果が得られます:
template<typename T>
std::string print_T() {
return __PRETTY_FUNCTION__;
}
std::string
のために戻ってます。
std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]
substr
と混合いくつかのfind
の魔法は、あなたが探して文字列表現を提供します。
他のヒント
本当に簡単な解決策: 型が何かを示す文字列オブジェクトを SomeClass のコンストラクターに渡すだけです。
例:
#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));
それを保存し、GetClassName の実装で表示するだけです。
もう少し複雑な解決策ですが、それでも非常に簡単です。
#define DEC_SOMECLASS(T, name) SomeClass<T> name; name.sType = #T;
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return sType.c_str(); }
std::string sType;
};
int main(int argc, char **argv)
{
DEC_SOMECLASS(int, s);
const char *p = s.GetClassName();
return 0;
}
テンプレート非タイプのソリューション:
独自の型 ID を作成し、ID と文字列表現を相互に変換する関数を持たせることもできます。
その後、型をテンプレートの非型パラメーターとして宣言するときに ID を渡すことができます。
template< typename T, int TYPEID>
struct SomeClass
{
const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};
...
SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;
次のようなことを試してみてください(これは私の頭の中に浮かんだものなので、コンパイルエラーなどが発生する可能性があることに注意してください)。
template <typename T>
const char* GetTypeName()
{
STATIC_ASSERT(0); // Not implemented for this type
}
#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }
// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return GetTypeName<T>; }
}
これは、追加したどのタイプでも機能します GETTYPENAME(type)
のライン。これには、関心のある型を変更せずに機能し、組み込み型やポインター型でも機能するという利点があります。ただし、使用するタイプごとに 1 行を記述する必要があるという明らかな欠点があります。
組み込みの RTTI を使用しない場合は、どこかに自分で情報を追加する必要があります (Brian R.Bondy の答えまたは dirdgently の答えが機能します。私の答えに加えて、その情報を追加するには 3 つの異なる場所があります。
- オブジェクトの作成時に使用する
SomeClass<int>("int")
- dirkgently のコンパイル時 RTTI または仮想関数を使用するクラス内
- 私のソリューションを使用したテンプレートを使用します。
3 つすべてが機能します。問題は、状況に応じてメンテナンスの問題が最も少なくなる場所を選択するだけです。
あなたによってはRTTIへのアクセス権を持っていない、それはあなたが型ID(T).nameの()を使用することはできません意味ですか?それはかなりコンパイラの助けを借りてそれを行うための唯一の方法だからです。
それは種類が一意の名前を持っている、または名前が何らかの形で永続化しようとしているため、非常に重要ですか?もしそうなら、あなたはそれらをコードで宣言されたクラスの名前だけよりも堅牢なものを与えることを検討すべきです。あなたは、異なる名前空間にそれらを置くことにより、2つのクラスに同じ非修飾名を付けることができます。また、Windows上の2つの異なるのDLLで(名前空間の資格を含む)と同じ名前を持つ2つのクラスを置くことができますので、あなたにも名前に含まれるDLLを識別する必要はあります。
これは、すべては、もちろん、あなたが文字列でやろうとしているかに依存します。
あなたは少しの魔法を自分で追加することができます。ような何かます:
#include <iostream>
#define str(x) #x
#define xstr(x) str(x)
#define make_pre(C) concat(C, <)
#define make_post(t) concat(t, >)
#define make_type(C, T) make_pre(C) ## make_post(T)
#define CTTI_REFLECTION(T, x) static std::string my_typeid() \
{ return xstr(make_type(T, x)); }
// the dark magic of Compile Time Type Information (TM)
#define CTTI_REFLECTION(x) static const char * my_typeid() \
{ return xstr(make_type(T, x)); }
#define CREATE_TEMPLATE(class_name, type) template<> \
struct class_name <type>{ \
CTTI_REFLECTION(class_name, type) \
};
// dummy, we'll specialize from this later
template<typename T> struct test_reflection;
// create an actual class
CREATE_TEMPLATE(test_reflection, int)
struct test_reflection {
CTTI_REFLECTION(test_reflection)
};
int main(int argc, char* argv[])
{
std::cout << test_reflection<int>::my_typeid();
}
私はインスペクタstatic
機能(ひいては非const
)作ってあげるます。
いいえ、申し訳ありません。
そして、あなたはint型でそれを使用しようとRTTIもコンパイルされません。