Boost.Variant で特定の型の値に対して処理を行う


C++
上記の記事では

  • apply<{期待する variant が保持する値の型}>({variant な変数}, {適用したい関数})

という定義になっていたのですが、これを

  • apply({variant な変数}, {期待する variant が保持する値の型を受け取る関数})

と、いう風にしてみました。

[ソース]

#include <boost/variant.hpp>
#include <boost/optional.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/result_type.hpp>


template<typename F, typename R>
struct apply_visitor : F{

    apply_visitor(F const& f) : F(f){ }

    using F::operator();

    typedef boost::optional<R> result_type;

    template<typename T>
    result_type
    operator ()(T) const{
        return boost::none;
    }
};


template<typename F>
struct apply_visitor<F, void> : F{
    typedef void result_type;

    apply_visitor(F const& f) : F(f){ }

    using F::operator();

    template<typename T>
    void
    operator ()(T) const{}
};


template<
    typename T, typename F,
    typename Result = typename boost::function_types::result_type<decltype(&F::operator())>::type,
    typename Visitor = apply_visitor<F, Result>
>
typename Visitor::result_type
apply(T const& variant, F func){
    return boost::apply_visitor(Visitor{ func }, variant);
}


template<
    typename T, typename F,
    typename Result = typename boost::function_types::result_type<decltype(&F::operator())>::type,
    typename Visitor = apply_visitor<F, Result>
>
typename Visitor::result_type
apply(T& variant, F func){
    return boost::apply_visitor(Visitor{ func }, variant);
}


#include <iostream>
#include <boost/optional/optional_io.hpp>

template<typename Op>
void
print_optional(Op op){
    if( op ){
        std::cout << op << std::endl;
    }
    else{
        std::cout << "NONE" << std::endl;
    }
}


int
main(){
    typedef boost::variant<int, float, char> var_type;
    var_type v1{42};
    var_type v2{3.14f};
    print_optional(apply(v1, [](int n ){ return n * n; }));
    print_optional(apply(v2, [](int n ){ return n * n; }));

    print_optional(apply(v1, [](float f){ return f + f; }));
    print_optional(apply(v2, [](float f){ return f + f; }));

    print_optional(apply(v1, [](int){ return "int"; }));
    print_optional(apply(v2, [](int){ return "int"; }));

    apply(v1, [](int& n){ std::cout << n << std::endl; n = 10; });
    std::cout << v1 << std::endl;

    return 0;
}

[出力]

 1764
NONE
NONE
 6.28
 int
NONE
42
10


面倒臭かったので関数オブジェクトしか受け取る事を考えていませんが、Generic Lambda も必要がないので C++11 でも動作しますね。
apply_visitor の戻り値型を optional にするのは有効っすなー。