C++ コンパイル時に値の型を知る方法
C++03以前にはそんなものはない
C++03以前には値の型を得ることのできるような仕組みは存在しません
それでも事前に登録しておいた型だけで構わないという限定的な条件の下でなら、似たような機能を実現することもできます
BOOST_TYPEOF
boost には BOOST_TYPEOF があります
しかし BOOST_TYPEOF は大掛かり過ぎるのでまずは簡単な方法で実装してみましょう
簡単な実装
まず値から直接的に型を得るようなことはできないので、template argument deduction を使って値から型に関連付けられている id を得ることにします
そしてこの id から関連付けられている型を得ることで機能を実現します
struct typeof_helper_t { struct type1 { char unused[1]; }; struct type2 { type1 unused[2]; }; typedef type1 typeid_bool; typedef type2 typeid_int; static typeid_bool get_typeid( bool ); static typeid_int get_typeid( int ); }; template<size_t> struct typeof_t; template<> struct typeof_t<sizeof(typeof_helper_t::typeid_bool)> { typedef bool type; }; template<> struct typeof_t<sizeof(typeof_helper_t::typeid_int)> { typedef int type; }; #define TYPEOF( N ) typeof_t<sizeof(typeof_helper_t::get_typeid(N))>::type
template argument deduction については「C++ 配列の要素数を静的に取得する方法」に譲ります
拡張可能な簡単な実装
上記の簡単な実装では型を登録するのにいちいち typeof_helper_t と typeof_t 編集しないといけなくて面倒でした
typeof_helper_t を適当なテンプレートに分けて、特殊化部分をマクロにすると簡単に拡張できそうです
template<size_t id> struct typeof_t; template<typename T> struct typeid_t; template<size_t id> struct typeid_helper_t { char unused[id]; }; struct typeof_helper_t { template<typename T> static typeid_helper_t<typeid_t<T>::value> get_typeid(T); }; #define TYPEOF( N ) typeof_t<sizeof(typeof_helper_t::get_typeid(N))>::type #define DEFINE_TYPEID( T, id ) \ template<> struct typeid_t<T> { static const size_t value = id; }; \ template<> struct typeof_t<id> { typedef T type; }; DEFINE_TYPEID( bool, 1 ) DEFINE_TYPEID( int, 2 ) DEFINE_TYPEID( const char*, 3 ) DEFINE_TYPEID( const wchar_t*, 4 )
使用例その1
例えば STATIC_PRINT マクロをこのようにすると int 以外の値も表示できるようになります
template<typename T,T N,bool = false> struct static_print_t { static void print(){ return static_print_t<T,N,true>::print(); } }; template<typename T,T N> struct static_print_t<T,N,true> {}; #define STATIC_PRINT( N ) static_print_t<TYPEOF(N),N>::print() STATIC_PRINT( true ); STATIC_PRINT( 1 );
使用例その2
例えば次のようにすると、値から型を推論してくれるようになって変数の型を明示的に指定する必要がなくなったりもします
#define AUTO( name, value ) TYPEOF(value) name = value int main(){ AUTO(aaa,"aaa"); AUTO(b,true); AUTO(n,100); debug_printf( "aaa=\"%s\",b=%d,n=%d\n", aaa, b, n ); return 0; }
BOOST_TYPEOF はいちいち型を登録しない
BOOST_TYPEOF はいちいち型を登録しなくても使えます
これは BOOST_TYPEOF の中で自動的に型の登録を行うようにしてあるからです
型の登録というのは上でもやっている型と番号の関連付けということですが、これを自動で行うためにはテンプレートで使えるコンパイル時カウンタが必要になってきます
boost の場合は encode_counter というテンプレートがそれです
興味があったら実装してみるのもおもしろいと思います