複数条件での enable_if

元ネタ:ボレロ村上 - ENiyGmaA Code - enable_switch - 複数の重複しうるコンパイル時条件で、SFINAE によるオーバーロードを書くには


SFINAE を使用する上で、この機能は欲しかったので自分も書いてみました。
二番煎じ乙。

#include <boost/utility/enable_if.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/count_if.hpp>

namespace detail{

namespace mpl = boost::mpl;

template<typename Seq, typename N, typename M>
struct slice :
    mpl::iterator_range<
        typename mpl::advance<typename mpl::begin<Seq>::type, N>::type,
        typename mpl::advance<typename mpl::begin<Seq>::type, M>::type
    >
{};

template<typename Seq, typename Pred>
struct is_only_one_if :
    mpl::equal_to<
        mpl::count_if<Seq, Pred>,
        mpl::int_<1>
    >
{};

using mpl::_1;

template<typename Seq, typename T>
struct apply :
    mpl::transform<Seq, mpl::apply1<_1, T> >{};

template<typename Seq, typename N, typename U>
struct enables_if_c_impl :
    boost::enable_if<
        // seq[N] && (count(seq[0, N+1], true) == 1)
        mpl::and_<
            mpl::at<Seq, N>,
            is_only_one_if<
                slice<
                    Seq,
                    mpl::int_<0>,
                    mpl::plus<N, mpl::int_<1> >
                >,
                mpl::equal_to<mpl::_1, mpl::true_>
            >
        >,
        U
    >
{};

template<typename Seq, typename N, typename T, typename U>
struct enables_if_impl :
    enables_if_c_impl<typename apply<Seq, T>::type, N, U>{};

}  // namespace detail

template<typename Seq, typename N, typename T, typename U = void>
struct enables_if : detail::enables_if_impl<Seq, N, T, U>{};

template<typename Seq, typename N, typename U = void>
struct enables_if_c : detail::enables_if_c_impl<Seq, N, U>{};


#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/always.hpp>
#include <string>
#include <iostream>

namespace mpl = boost::mpl;

using mpl::_1;

typedef mpl::vector<
    boost::is_same<int, _1>,
    boost::is_integral<_1>,
    boost::is_class<_1>,
    mpl::always<mpl::true_>
> enables;

template<typename T>
void
test(T t, typename enables_if<enables, mpl::int_<0>, T>::type* =0){
    std::cout << "int:" << t << std::endl;
}

template<typename T>
void
test(T t, typename enables_if<enables, mpl::int_<1>, T>::type* =0){
    std::cout << "long:" << t << std::endl;
}

template<typename T>
void
test(T t, typename enables_if<enables, mpl::int_<2>, T>::type* =0){
    std::cout << "class:" << t << std::endl;
}

template<typename T>
void
test(T t, typename enables_if<enables, mpl::int_<3>, T>::type* =0){
    std::cout << "other:" << t  << std::endl;
}

int
main(){
    test(0);
    test(long(100));
    test(std::string("str"));
    test(0.0f);
    
    return 0;
}

[出力]

int:0
long:100
class:str
other:0


機能的には元ネタに劣るけど、とりあえず、これでいいかなーと。
Variadic Templates 使いたい……。

[boost]
var 1.46.1
[参照]
http://d.hatena.ne.jp/boleros/20110222/1298394129