Boost.TTI
レビューが終わったのが8月なので今更かよ!って感じですが。年内のうちに書いておかったので書きます。
一部のメタプログラマ歓喜の Type Traits Introspection ライブラリです。
[Type Traits Introspection Library]
Boost.TTI はある型が、メンバやメソッドを持っているかどうかを判断するためのライブラリです。
BOOST_MPL_HAS_XXX_TRAIT_DEF の拡張版だと思ってもらうのが分かりやすいと思います。
正式にリリース前なので本記事の内容はリリース時に仕様が変更される可能性もあるので注意して下さい。
[簡単な使い方]
BOOST_MPL_HAS_XXX_TRAIT_DEF と同様にマクロ関数を使用して、メタ関数を定義します。
#include <boost/tti/has_type.hpp> #include <boost/tti/has_member_function.hpp> #include <boost/tti/has_member_data.hpp> #include <boost/mpl/assert.hpp> namespace mpl = boost::mpl; // has_xxx を定義する BOOST_TTI_HAS_TYPE(value_type) // has_type_value_type BOOST_TTI_HAS_MEMBER_DATA(value) // has_member_data_value BOOST_TTI_HAS_MEMBER_FUNCTION(func) // has_member_function_funcg struct X{ typedef int value_type; value_type value; void func(int, int){} int func(float){ return 0; } }; // value_type の有無 BOOST_MPL_ASSERT((has_type_value_type<X>)); // value_type の型を指定してチェックも出来る BOOST_MPL_ASSERT ((has_type_value_type<X, int>)); BOOST_MPL_ASSERT_NOT((has_type_value_type<X, void>)); // プロパティ value の有無 // value の型を指定する BOOST_MPL_ASSERT ((has_member_data_value<X, X::value_type>)); BOOST_MPL_ASSERT_NOT((has_member_data_value<X, float>)); // メソッド func の有無 // 戻り値型と引数型を指定する BOOST_MPL_ASSERT(( has_member_function_func< X, void, // 戻り値型 mpl::vector<int, int> // 引数は mpl::vector で渡す > )); // オーバーロードしていてもチェックできる BOOST_MPL_ASSERT(( has_member_function_func< X, int, mpl::vector<float> > )); BOOST_MPL_ASSERT_NOT((has_member_function_func<X, void>)); int main(){}
こんな感じで定義したメタ関数に型を渡して使用します。
また、BOOST_TTI_TRAIT_{macro name} でメタ関数名を指定することも出来ます。
// has_value というメタ関数名で定義 BOOST_TTI_TRAIT_HAS_MEMBER_DATA(has_value, value) struct X{ int value; }; BOOST_MPL_ASSERT((boost::tti::has_value<X, int>));
[::type を展開する]
さて、メタプログラミングを行なっている時に次のように ::type を展開して欲しい場合があると思います。
#include <boost/tti/has_type.hpp> BOOST_TTI_HAS_TYPE(value_type) template<typename T> struct element_value_type{ typedef typename T::value_type type; }; template<typename T> struct X // <mpl::identity<T>::type, element_type<T>::type> と展開して欲しい : has_type_value_type<mpl::identity<T>, element_value_type<T> >{};
残念ながら has_type_value_type 内では ::type の展開を行いません。
そこで、Boost.TTI で用意されているヘルパ関数を使用します。
#include <boost/tti/has_type.hpp> // 追加 #include <boost/tti/mf/mf_has_type.hpp> BOOST_TTI_HAS_TYPE(value_type) template<typename T> struct element_value_type{ typedef typename T::value_type type; }; template<typename T> struct X // boost::tti::mf_has_type // Boost.MPL の placeholders を使用して部分適用される : boost::tti::mf_has_type< has_type_value_type<mpl::_1, mpl::_2>, mpl::identity<T>, element_value_type<T> > {};
これでhas_type_value_type に ::type を展開した結果が適用されます。
[実際に使ってみる]
次のような関数を定義してみたいと思います。
1.T が value_type 型の value をメンバとして持っている場合は、value を返す
2.それ以外の場合は、T を返す
実際に書いてみるとこんな感じになりました。
#include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_class.hpp> #include <boost/mpl/and.hpp> #include <boost/tti/has_type.hpp> #include <boost/tti/mf/mf_has_type.hpp> #include <boost/tti/has_member_data.hpp> #include <boost/tti/mf/mf_has_member_data.hpp> namespace mpl = boost::mpl; BOOST_TTI_HAS_TYPE(value_type) // has_type_value_type BOOST_TTI_HAS_MEMBER_DATA(value) // has_member_data_value template<typename T> struct element_value_type{ typedef typename T::value_type type; }; template<typename T> struct trivial_get_value : mpl::and_< boost::is_class<T>, has_type_value_type<T>, boost::tti::mf_has_member_data< has_member_data_value<mpl::_1, mpl::_2>, mpl::identity<T>, element_value_type<T> > >::type {}; template<typename T> typename T::value_type get_value(T const& t, typename boost::enable_if<trivial_get_value<T> >::type* =0){ return t.value; } template<typename T> T const& get_value(T const& t, typename boost::disable_if<trivial_get_value<T> >::type* =0){ return t; } struct X{ typedef int value_type; value_type value; }; struct hoge{}; int main(){ X x = {4}; int n = get_value(x); hoge h; get_value(h); get_value(0); return 0; }
[注意]
Boost.TTI のメタ関数にクラス以外の型を渡すとコンパイルエラー(false が返ってくるわけではなく)になるので注意して下さい。
上記のソースもそのために boost::is_same でチェックを行っています。