mpl::switch_ で、タグディスパッチ

んーこれであってるのかな?
何気にタグディスパッチは初めて使ったかも知れない。
mpl::switch_ と組み合わせてみました。

#include <boost/mpl/has_xxx.hpp>
#include <boost/mpl/switch.hpp>
#include <boost/mpl/always.hpp>
#include <boost/mpl/vector.hpp>

namespace mpl = boost::mpl;

template<typename Vec, int N>
struct at_c_impl{
    
    BOOST_MPL_HAS_XXX_TRAIT_DEF(at);
    BOOST_MPL_HAS_XXX_TRAIT_DEF(at_x);
    BOOST_MPL_HAS_XXX_TRAIT_DEF(at_y);
    
    struct at_tag{};
    struct at_x_tag{};
    struct at_y_tag{};
    struct default_tag{};
    
    // 複数の条件から、使用する tag を取得する
    typedef mpl::vector<
        mpl::pair<has_at<mpl::_1>,                  mpl::always<at_tag> >,
        mpl::pair<mpl::always<mpl::bool_<N == 0> >, mpl::always<at_x_tag > >,
        mpl::pair<mpl::always<mpl::bool_<N == 1> >, mpl::always<at_y_tag > >,
        mpl::pair<mpl::always<mpl::true_>,          mpl::always<default_tag> >
    > tags;
    
    typedef typename Vec::value_type const& result_type;
    
    result_type
    operator ()(Vec const& vec) const{
        return (*this)(vec, typename mpl::switch_<tags, Vec>::type());
    };
private:
    // tag によって処理を切り替える
    result_type
    operator ()(Vec const& vec, at_tag) const{
        return typename Vec::at()(vec, N);
    };
    result_type
    operator ()(Vec const& vec, at_x_tag) const{
        return typename Vec::at_x()(vec);
    };
    result_type
    operator ()(Vec const& vec, at_y_tag) const{
        return typename Vec::at_y()(vec);
    };
    result_type
    operator ()(Vec const& vec, default_tag) const{
        // なんか適当な処理
        return vec[N];
    };
};

template<int N, typename Vec>
typename at_c_impl<Vec, N>::result_type
at_c(Vec const& vec){
    return at_c_impl<Vec, N>()(vec);
}


#include <iostream>

struct vec2{
    typedef float value_type;
    value_type x, y;
    
    // has_xxx で判定するので、関数オブジェクト
    struct at_x{
        value_type const&
        operator ()(vec2 const& vec) const{
            return vec.x;
        }
    };
    struct at_y{
        value_type const&
        operator ()(vec2 const& vec) const{
            return vec.y;
        }
    };
};

struct vec3{
    typedef float value_type;
    value_type value[3];
    
    // has_xxx で判定するので、関数オブジェクト
    struct at{
        value_type const&
        operator ()(vec3 const& vec, int n) const{
            return vec.value[n];
        }
    };
    
    // 複数定義されていても、at が優先的に使用される
    struct at_x{
        value_type const&
        operator ()(vec3 const& vec) const{
            // テスト用
            static value_type test = 9.99999f;
            return test;
//            return vec.value[0];
        }
    };
};


int
main(){
    vec3 v3 = {{ 1.0f, 2.0f, 3.0f }};
    std::cout << at_c<0>(v3) << std::endl;  // != 9.99999f 
    std::cout << at_c<1>(v3) << std::endl;
    std::cout << at_c<2>(v3) << std::endl;
    
    vec2 v2  = {3.14f, 3.14f};
    std::cout << at_c<0>(v2) << std::endl;
    std::cout << at_c<1>(v2) << std::endl;
    
    return 0;
}

[出力]

1
2
3
3.14
3.14

んーこんな感じでしょうか。
Vec::at が定義されていれば、そちらを優先的に使用し、Vec::at_x が定義されていれば、そちらを使用します。
at_x を実装するならその逆
class ないで、オブジェクト関数を定義しているのは、has_xxx で、判断を行う為です。
ちょっとめんどくさいけどこれが確実かな?
複数条件で、SFINAE する場合はこっちの方が楽かも知れない。


[boost]
ver 1.46.1