Boost.Proto の式を型推論で評価する

うーん、こんな感じ?

#include <boost/proto/proto.hpp>
#include <iostream>

namespace proto = boost::proto;

//--------------------------------------
// 適当なベクトル型
struct vec{
    int value[3];
};

vec
make_vec(int x, int y, int z){
    vec v = {{x, y, z}};
    return v;
}

//--------------------------------------
// Boost.Proto
struct vec_context
    : proto::callable_context<const vec_context>{
    
    typedef int result_type;
    vec_context(int index) : index_(index){}
    
    result_type
    operator ()(proto::tag::terminal, vec const& v) const{
        return v.value[index_];
    }
    result_type
    operator ()(proto::tag::terminal, int const& n) const{
        return n;
    }
private:
    int index_;
};

// 演算を評価するための定義
struct grammar :
    proto::or_<
        proto::terminal<int>,
        proto::terminal<vec>,
        proto::plus<grammar, grammar>,
        proto::minus<grammar, grammar>,
        proto::multiplies<grammar, grammar>,
        proto::divides<grammar, grammar>
    >
{};

template<typename T>
struct vec_expr;

struct vec_domain :
    proto::domain<proto::generator<vec_expr>, grammar>{};

template<typename T>
struct vec_expr
    : proto::extends<T, vec_expr<T>, vec_domain>{
    explicit vec_expr(T const& t) : proto::extends<T, vec_expr<T>, vec_domain>(t){}
    
    // 一部の要素のみの評価を行う
    int operator [](std::size_t i) const{
        std::cout << "expression" << std::endl;
        return proto::eval(*this, vec_context(i) );
    }
    
    // vec 型へ型推論する際に評価を行う
    operator vec() const{
        vec result;
        for(int i = 0 ; i < 3 ; ++i){
            result.value[i] = (*this)[i];
        }
        return result;
    }
};

// 演算の定義
template<typename>
struct is_vec : boost::mpl::false_{};
template<>
struct is_vec<vec> : boost::mpl::true_{};
BOOST_PROTO_DEFINE_OPERATORS(is_vec, vec_domain);


int
main(){
    vec v  = make_vec(10, 20, 30);
    vec v2 = make_vec(30, 20, 30);
    
    // vec 型に型推論する際に、評価が行われる
    vec result = (v + v2) * 2;
    
    std::cout << result.value[0] << std::endl;
    std::cout << result.value[1] << std::endl;
    std::cout << result.value[2] << std::endl;
    
    // 一部の要素のみ、評価を行う
    std::cout << (v + result)[0] << std::endl;
    
    return 0;
}

[出力]

expression
expression
expression
80
80
120
expression
90


Boost.Proto で演算を行うと vec_expr 型を返すので、vec_expr に、operator vec() を定義すれば、型推論を行うことが出来ます。
これで、ユーザコードレベルで、proto::eval を行う必要がありません。
やったね!


[boost]
ver 1.46.1