方針
元のコードに対して何か加えるようなことはしない
外部ライブラリを使っている場合、元のクラス定義に手動でクラス名を文字列として定義するようなことはできない。
struct SomeStruct{
/*なんらかの実装*/
static constexpr std::string_view name = "SomeStruct";
};
typoで構造体名と文字列が違うというミスを犯す可能性があるので却下。
テンプレート引数で渡された型に対しても使える
以下のようなマクロを定義すれば一応できているようには見える。
#define ToString(X) #X
struct SomeStruct{};
int main(){
std::cout << ToString(SomeStruct) << std::endl; // "SomeStruct"と表示される
}
しかし、テンプレート引数に対しては無力である。
#define ToString(X) #X
struct SomeStruct{};
template<typename T>
void func(){
std::cout << ToString(T) << std::endl; // "T"と表示される
}
int main(){
func<SomeStruct>();
}
これでは、意味がないのでテンプレート引数に対しても正しく表示できるような実装を目指す。
実装方法
C++には__func__
という事前定義識別子がある。これは関数名を文字列として取得できる。
struct DDD{};
template<typename T>
void IwantFuncName(){
std::cout << __func__ << std::endl; // "IwantFuncName"と表示される
}
int main(){
IwantFuncName<DDD>();
}
しかし、(実装定義ではあるが)関数名だけであるので戻り値型や引数、テンプレート引数などは表示されない。
gccとclangには似たような識別子として__PRETTY_FUNCTION__
というものがあり、これは__func__
では表示されなかったテンプレート引数などを事細かに文字列として取得できる。
struct DDD{};
template<typename T>
void IwantFuncName(){
std::cout << __PRETTY_FUNCTION__ << std::endl;
// gccだと"void IwantFuncName() [with T = DDD]"
// clangだと"void IwantFuncName() [T = DDD]"
}
int main(){
IwantFuncName<DDD>();
}
gccとclangでは上記の方法で大丈夫だが、MSVCは__FUNCDNAME__
はマングリングされた名前となり、__FUNCTION__
では非メンバテンプレート関数の場合、関数名しか取得できない。
struct DDD {};
template<typename T>
void IwantFuncName() {
std::cout << __FUNCTION__ << std::endl; // "IwantFuncName"と表示される
}
int main() {
IwantFuncName<DDD>();
}
しかし、__FUNCTION__
で取得する関数がクラステンプレートの(静的)メンバ関数の場合、クラステンプレートのテンプレート引数に渡されたクラス名が取得できるようになる。
struct DDD {};
template<typename T>
struct class_name{
static void IwantFuncName() {
std::cout << __FUNCTION__ << std::endl;
// "class_name<struct DDD>::IwantFuncName"と表示される
}
};
int main() {
class_name<DDD>{}.IwantFuncName();
}
それぞれ取得できる文字列はコンパイラ次第なので、これを処理してやることでテンプレート引数に渡した型名が取得できる。
ゴリゴリに実装依存では?
実装
template<typename D>
class class_name {
private:
static constexpr auto impl(){
#ifdef _MSC_VER
constexpr auto str_ptr = __FUNCTION__;
constexpr auto base_class_len = std::char_traits<char>::length("class_name<");
constexpr auto head_len = base_class_len + std::char_traits<char>::length(str_ptr[base_class_len] == 's' ? "struct " : "class ");
constexpr auto tail_len = std::char_traits<char>::length(">::impl");
#elif defined(__clang__)
constexpr auto str_ptr = __PRETTY_FUNCTION__;
constexpr auto head_len = std::char_traits<char>::find(str_ptr, std::char_traits<char>::length(str_ptr), '[') - str_ptr + std::char_traits<char>::length("[D = ");
constexpr auto tail_len = std::char_traits<char>::length("]");
#elif defined(__GNUC__)
constexpr auto str_ptr = __PRETTY_FUNCTION__;
constexpr auto head_len = std::char_traits<char>::find(str_ptr, std::char_traits<char>::length(str_ptr), '[') - str_ptr + std::char_traits<char>::length("[with D = ");
constexpr auto tail_len = std::char_traits<char>::length("]");
#endif
constexpr auto len = std::char_traits<char>::length(str_ptr);
return std::string_view(&str_ptr[head_len], len - head_len - tail_len);
}
public:
static constexpr auto value = impl();
};
コード全体
用例
struct SomeStruct{};
int main(){
std::cout << class_name<SomeStruct>::value << std::endl; // "SomeStruct"と表示される
}
もちろんテンプレート引数に対しても正確に取得できる。
struct SomeStruct{};
template<typename T>
void func(){
std::cout << class_name<T>::value << std::endl; // "SomeStruct"と表示される
}
int main(){
func<SomeStruct>();
}