テンプレートテンプレートパラメータに Boost.MPL のラムダ式を渡す

以下のようにテンプレートテンプレートパラメータを受け取るテンプレートクラスがあるとします。

template<
    typename T,
    typename U,
    template<typename, typename> class Pred = boost::is_same
>
struct equal{
    typedef Pred<T, U> type;
};

// OK
BOOST_MPL_ASSERT(( equal<int, int> ));
// OK
BOOST_MPL_ASSERT(( equal<mpl::int_<11>, mpl::long_<11>, mpl::equal_to> ));

上記のクラスでは、型の比較を行うためのテンプレートクラスを引数に受け取って処理を行っています。


しかし、上記のようにテンプレートテンプレートパラメータで受け取ると、テンプレート引数が2つのクラスしか受け取る事が出来ないので、次のような書き方は出来ません。

// NG
BOOST_MPL_ASSERT((
    equal<
        mpl::vector<int, char, float>,
        mpl::vector<int, char, float>,
        mpl::equal        // mpl::equal はテンプレートパラメータが3つあるので渡せない
    >::type
));


これは、mpl::equal が、3つのテンプレート引数で定義されている為、template class に渡すことができないのが原因です。

// mpl::equal
template<
    typename Seq1,
    typename Seq2,
    typename Pred = is_same<_1, _2>
>
struct equal;


と、いうことで、こういう場合に対応させるためのラッパーを簡単に書いてみました。

[ソース]

#include <boost/mpl/assert.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/long.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/type_traits/is_same.hpp>

namespace mpl = boost::mpl;

// Boost.MPL の Lambda Expression の評価を行う為のラッパー
template<typename Expr>
struct applicable{
    // テンプレートテンプレートパラメータによって渡す
    template<typename T0>
    struct apply1 : mpl::apply1<Expr, T0>{};
    
    template<typename T0, typename T1>
    struct apply2 : mpl::apply2<Expr, T0, T1>{};
};

template<
    typename T,
    typename U,
    template<typename, typename> class Pred = boost::is_same
>
struct equal{
    typedef Pred<T, U> type;
};

BOOST_MPL_ASSERT(( equal<int, int> ));
BOOST_MPL_ASSERT(( equal<mpl::int_<11>, mpl::long_<11>, mpl::equal_to> ));

BOOST_MPL_ASSERT((
    equal<int, char, applicable<boost::is_same<mpl::_1, int> >::apply2>::type
));

// OK
BOOST_MPL_ASSERT((
    equal<
        mpl::vector<int, char, float>,
        mpl::vector<int, char, float>,
        applicable<mpl::equal<mpl::_1, mpl::_2,
            mpl::quote2<boost::is_same> >
        >::apply2    // テンプレートテンプレートパラメータに合わせる
    >::type
));

int
main(){
    
    return 0;
}

こんな感じで、テンプレートテンプレートパラメータに渡すことが出来るように Boost.MPL のラムダ式をラップして評価しています。
仕組み自体はそんなに難しくないかな。
ところで、Boost.MPL で似たようなことって出来ませんかね。

[おまけ1]

件の問題が、テンプレートテンプレートパラメータの数だけであれば、Variadic Templates を使用すれば解決します。

template<
    typename T,
    typename U,
    template<typename...> class Pred = boost::is_same
>
struct equal{
    typedef Pred<T, U> type;
};

// OK
BOOST_MPL_ASSERT((
    equal<
        mpl::vector<int, char, float>,
        mpl::vector<int, char, float>,
        mpl::equal
    >
));

[おまけ2]

そもそもテンプレートテンプレートパラメータを使用するのではなくて、Boost.MPL の apply で評価したほうがいいんじゃないかなーと思いますね。
mpl::equal も同じように処理していますし。

template<
    typename T,
    typename U,
    // Lambda Expression を引数に受け取る
    typename Pred = boost::is_same<mpl::_1, mpl::_2>
>
struct equal{
    // mpl::apply で評価
    typedef typename mpl::apply2<Pred, T, U>::type type;
};

// OK
BOOST_MPL_ASSERT(( equal<
    mpl::int_<11>,
    mpl::long_<11>,
    mpl::equal_to<mpl::_1, mpl::_2>
> ));
// OK
BOOST_MPL_ASSERT((
    equal<
        mpl::vector<int, char, float>,
        mpl::vector<int, char, float>,
        mpl::equal<mpl::_1, mpl::_2, mpl::quote2<boost::is_same> >
    >::type
));