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(){}