template class に対応した、is_same の実装
前回:http://d.hatena.ne.jp/osyo-manga/20110429/1304095021
とりあえず、やりたい事は出来た感じです。
以下、Variadic Templates 禁止。
☆使い方
#include <vector> #include <string> #include <map> #include <boost/mpl/assert.hpp> #include <boost/mpl/vector.hpp> #include <boost/tuple/tuple.hpp> #include <boost/variant/variant.hpp> template<typename T, typename Tag> struct fill; struct _{}; template< typename T, typename U, typename Pred = is_same_impl<boost::mpl::_1, boost::mpl::_2> > struct is_same; namespace mpl = boost::mpl; BOOST_MPL_ASSERT (( is_same<int, int> )); BOOST_MPL_ASSERT_NOT(( is_same<int, int&> )); // _ を使用すすることで、部分的に比較が出来る BOOST_MPL_ASSERT(( is_same< std::vector<_>, std::vector<int> > )); // std::string か判定 template<typename T> struct is_string : is_same<std::basic_string<_>, T>{}; BOOST_MPL_ASSERT (( is_string< std::string > )); BOOST_MPL_ASSERT (( is_string< std::wstring > )); BOOST_MPL_ASSERT_NOT(( is_string< char const* > )); // ピンポイントな判定 template<typename T> struct is_map_key_int : is_same<std::map<int, _>, T>{}; BOOST_MPL_ASSERT (( is_map_key_int< std::map<int, std::string> > )); BOOST_MPL_ASSERT_NOT(( is_map_key_int< std::map<std::string, int> > )); // ネストも出来る BOOST_MPL_ASSERT(( is_same< std::pair<std::pair<int, _>, std::pair<_, std::string> >, std::pair<std::pair<int, std::string>, std::pair<int, std::string> > > )); // 引数の多い(可変長な) class の判定は、fill を使用する BOOST_MPL_ASSERT(( is_same< fill<boost::variant<>, _>::type, // boost::variant<_, _, ...> boost::variant<int, float, char> > )); BOOST_MPL_ASSERT(( is_same< boost::tuple<int, _, char>, boost::tuple<int, float, char> > )); int main(){}
☆is_same
基本的に boost::is_same と使い方は同じです。
BOOST_MPL_ASSERT (( is_same<int, int> )); BOOST_MPL_ASSERT_NOT(( is_same<int, int&> ));
_ を使用することで、template 引数の部分的な比較を行うことが出来ます。
BOOST_MPL_ASSERT(( is_same< std::pair<_, _>, // 第一引数と第二引数はなんでもいい std::pair<int, char> > )); BOOST_MPL_ASSERT(( is_same< std::pair<int, _>, // 第一引数は、int でなければいけない std::pair<int, std::string> > ));
is_same の内部では、
mpl::and_<Pred<_, int>, Pred<_, char> >
の様に、template 引数ごとに Pred を使用して、比較を行っています。
また、fill メタ関数を使用することで、引数の多い(可変長な) class の is_xxx を作成することが出来ます。
template<typename T> struct is_tuple : is_same< fill<boost::tuple<>, _>::type, // boost::tuple<_, _, ...> T >{}; BOOST_MPL_ASSERT(( is_tuple<boost::tuple<int, char, float> > ));
こんな感じで is_xxx を簡単に書くことが出来ます。
☆各メタ関数の断片的な中身
実装は、特殊化を行い、template class を判別しています。
ごちゃごちゃしているけど、そんなに難しい事はしていない…はず。
template<typename T, typename Tag> struct fill; template< template<typename> class T, typename Arg0, typename Tag > struct fill< T<Arg0>, Tag > : boost::mpl::identity<T<Tag> >{}; template< template<typename, typename> class T, typename Arg0, typename Arg1 typename Tag > struct fill< T<Arg0, Arg1>, Tag > : boost::mpl::identity<T<Tag, Tag> >{}; // template class を直接受け取って、Tag を埋める // fill2<std::vector, Tag> template< template<typename> class T, typename Tag > struct fill1 : boost::mpl::identity< T<Tag> >{}; template< template<typename, typename> class T, typename Tag > struct fill2 : boost::mpl::identity< T<Tag, Tag> >{}; template< typename T, typename U, typename Pred = is_same_impl<boost::mpl::_1, boost::mpl::_2> > struct is_same : boost::mpl::apply<Pred, T, U>::type{}; template< template <typename> class TT, typename TArg0, typename UArg0, typename Pred > struct is_same<TT<TArg0>, TT<UArg0>, Pred> : is_same<TArg0, UArg0, Pred>::type{}; template< template <typename, typename> class TT, typename TArg0, typename TArg1, typename UArg0, typename UArg1, typename Pred > struct is_same< TT<TArg0, TArg1>, TT<UArg0, UArg1>, Pred > : boost::mpl::bool_< is_same<TArg0, UArg0, Pred>::value, && is_same<TArg1, UArg1, Pred>::value >{}; template< template <typename, typename, typename> class TT, typename TArg0, typename TArg1, typename TArg2, typename UArg0, typename UArg1, typename UArg2, typename Pred > struct is_same< TT<TArg1, TArg2, TArg3>, TT<UArg1, UArg2, UArg3>, Pred > : boost::mpl::bool_< is_same<TArg0, UArg0, Pred>::value, && is_same<TArg1, UArg1, Pred>::value, && is_same<TArg2, UArg2, Pred>::value >{};
実際は、Boost.PP を使用して実装を行っています。
mpl::and_ だとちょっと展開がややこしかったので、素直に && と mpl::bool_ を使用しています。
mpl::and_ には、デフォルトだと5つまでしか渡せないので、ぐぬぬ……。
と、まぁとりあえず、こんな感じです。
[boost]
ver 1.46.1
Boost.PP 適用後の全てのソースは以下から。
#include <boost/mpl/bool.hpp> #include <boost/mpl/apply.hpp> #include <boost/mpl/identity.hpp> #include <boost/preprocessor/repeat_from_to.hpp> #include <boost/preprocessor/facilities/intercept.hpp> #include <boost/preprocessor/enum_params.hpp> #include <boost/preprocessor/arithmetic/sub.hpp> template<typename T, typename Tag> struct fill; #if defined BOOST_NO_VARIADIC_TEMPLATES #ifndef FILL_LIMIT_TYPES # define FILL_LIMIT_TYPES 21 #endif #define PP_TYPENAMES(n) BOOST_PP_ENUM_PARAMS(n, typename BOOST_PP_INTERCEPT) #define PP_TYPENAMES_ARGS(n) BOOST_PP_ENUM_PARAMS(n, typename Arg) #define PP_ARGS(n) BOOST_PP_ENUM_PARAMS(n, Arg) #ifndef PP_FILL_TEMPLAETS_CLASS #define PP_FILL_TEMPLAETS_CLASS(z, n, text) \ template< \ template< PP_TYPENAMES(n) > class T, \ PP_TYPENAMES_ARGS(n), \ typename Tag \ > \ struct fill< T<PP_ARGS(n)>, Tag> : \ boost::mpl::identity< \ T< BOOST_PP_ENUM_PARAMS(n, Tag BOOST_PP_INTERCEPT) > \ >{}; #endif BOOST_PP_REPEAT_FROM_TO(1, FILL_LIMIT_TYPES, PP_FILL_TEMPLAETS_CLASS, _) #undef PP_FILL_TEMPLAETS_CLASS #ifndef PP_FILL_N_TEMPLATES_CLASS #define PP_FILL_N_TEMPLATES_CLASS(z, n, m) \ template< \ template < PP_TYPENAMES(n) > class T, \ typename Tag \ > \ struct BOOST_PP_CAT(fill, n) : \ boost::mpl::identity< \ T< BOOST_PP_ENUM_PARAMS(n, Tag BOOST_PP_INTERCEPT) > \ >{}; #endif BOOST_PP_REPEAT_FROM_TO(1, FILL_LIMIT_TYPES, PP_FILL_N_TEMPLATES_CLASS, _) #undef PP_FILL_N_TEMPLATES_CLASS #undef PP_TYPENAMES #undef PP_TYPENAMES_ARGS #undef PP_ARGS // Variadic Templates 版 #else #include <boost/type_traits/conditional.hpp> template< template<typename...> class T, typename ...Args, typename Tag > struct fill<T<Args...>, Tag> : boost::mpl:: identity< T<typename boost::conditional<true, Tag, Args>::type...> >{}; #ifndef FILL_LIMIT_TYPES # define FILL_LIMIT_TYPES 21 #endif #ifndef PP_FILL_N_TEMPLATES_CLASS #define PP_FILL_N_TEMPLATES_CLASS(z, n, text) \ template< \ template <typename...> class T, \ typename Tag \ > \ struct BOOST_PP_CAT(fill, n) : \ boost::mpl::identity< \ T< BOOST_PP_ENUM_PARAMS(n, Tag BOOST_PP_INTERCEPT) > \ >{}; #endif BOOST_PP_REPEAT_FROM_TO(1, FILL_LIMIT_TYPES, PP_FILL_N_TEMPLATES_CLASS, _) #undef PP_FILL_N_TEMPLATES_CLASS #endif #ifndef IS_SAME_LIMIT_TYPES # define IS_SAME_LIMIT_TYPES 21 #endif struct _{}; template<typename T, typename U> struct is_same_impl : boost::mpl::false_{}; template<typename T> struct is_same_impl<T, T> : boost::mpl::true_{}; template<typename T> struct is_same_impl<T, _> : boost::mpl::true_{}; template<typename T> struct is_same_impl<_, T> : boost::mpl::true_{}; template<> struct is_same_impl<_, _> : boost::mpl::true_{}; template< typename T, typename U, typename Pred = is_same_impl<boost::mpl::_1, boost::mpl::_2> > struct is_same : boost::mpl::apply<Pred, T, U>::type{}; template< template <typename> class TT, typename TArg0, typename UArg0, typename Pred > struct is_same<TT<TArg0>, TT<UArg0>, Pred> : is_same<TArg0, UArg0, Pred>::type{}; #define PP_AND_IS_SAME(z, n, text) \ && is_same< \ BOOST_PP_CAT(TArg, n), \ BOOST_PP_CAT(UArg, n), \ Pred \ >::value #define PP_IS_SAME_CLASS(z, n, text) \ template< \ template < BOOST_PP_ENUM_PARAMS(n, typename BOOST_PP_INTERCEPT) > \ class TT, \ BOOST_PP_ENUM_PARAMS(n, typename TArg), \ BOOST_PP_ENUM_PARAMS(n, typename UArg), \ typename Pred \ > \ struct is_same< \ TT< BOOST_PP_ENUM_PARAMS(n, TArg) >, \ TT< BOOST_PP_ENUM_PARAMS(n, UArg) >, \ Pred \ > : \ boost::mpl::bool_< \ is_same<TArg0, UArg0, Pred>::value \ BOOST_PP_REPEAT(BOOST_PP_SUB(n, 1), PP_AND_IS_SAME, _) \ >{}; BOOST_PP_REPEAT_FROM_TO(2, IS_SAME_LIMIT_TYPES, PP_IS_SAME_CLASS, _) #undef PP_AND_IS_SAME #undef PP_IS_SAME_CLASS //---------------------------------------------------------- // 適当なテスト #include <vector> #include <string> #include <map> #include <boost/mpl/equal.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/quote.hpp> #include <boost/mpl/vector.hpp> #include <boost/tuple/tuple.hpp> #include <boost/variant/variant.hpp> namespace mpl = boost::mpl; BOOST_MPL_ASSERT(( is_same<int, int> )); BOOST_MPL_ASSERT_NOT(( is_same<int, int&> )); BOOST_MPL_ASSERT(( is_same< std::vector<_>, std::vector<int> > )); BOOST_MPL_ASSERT_NOT(( is_same< std::vector<_>, int > )); template<typename T> struct is_string : is_same<std::basic_string<_>, T>{}; BOOST_MPL_ASSERT(( is_string< std::string > )); BOOST_MPL_ASSERT(( is_string< std::wstring > )); BOOST_MPL_ASSERT_NOT(( is_string< char const* > )); template<typename T> struct is_map_key_int : is_same<std::map<int, _>, T>{}; BOOST_MPL_ASSERT(( is_map_key_int< std::map<int, std::string> > )); BOOST_MPL_ASSERT_NOT(( is_map_key_int< std::map<std::string, int> > )); BOOST_MPL_ASSERT(( is_same< std::pair<std::pair<int, _>, std::pair<_, std::string> >, std::pair<std::pair<int, std::string>, std::pair<int, std::string> > > )); BOOST_MPL_ASSERT_NOT(( is_same< std::pair<std::pair<int, _>, std::pair<_, std::string> >, std::pair<std::pair<std::string, int>, std::pair<std::string, int> > > )); BOOST_MPL_ASSERT(( is_same< fill<boost::variant<>, _>::type, boost::variant<int, float, char> > )); BOOST_MPL_ASSERT(( is_same< fill<boost::tuple<>, _>::type, boost::tuple<int, float, char> > )); template<typename T> struct is_tuple : is_same<fill<boost::tuple<>, _>::type, T>{}; BOOST_MPL_ASSERT(( is_tuple<boost::tuple<int, char, float> > )); BOOST_MPL_ASSERT(( is_same< boost::tuple<int, _, char>, boost::tuple<int, float, char> > )); BOOST_MPL_ASSERT_NOT(( is_same< boost::tuple<int, _, char>, boost::tuple<int, float, int> > )); int main(){}