I played with Boost.Proto, mainly for fun, and to see if I will use it in my projects in the future. Nevertheless, as probably most of the newcomers to this library, I played with a modified version of the "lazy vector", but instead of transformations, I performed evaluations instead of contexts. A vector is defined as follows (well, I know, "vector" is not a good name for what is defined in the global scope of the namespace ...)
template <std::size_t D, class T>
class vector {
T data_[D];
enum { dimension = D };
};
template <class> class vector_expr;
this is a template for the measurement and data type, such as boost :: array (I did not use this since I would like to overload operator = to accept expression trees, as is usually done in such things). I defined scalars using the code from the proto-guide
struct scalar_terminal :
proto::terminal<proto::convertible_to <double> >
{};
template <class T> struct is_vector : mpl::false_ {};
template <std::size_t D, class T> struct is_vector <vector <D, T> > : mpl::true_ {};
struct vector_terminal :
proto::and_ <
proto::terminal<_>
, proto::if_<is_vector<proto::_value>()>
>
{};
struct vector_domain
: proto::domain <proto::generator <vector_expr> >
{};
template <class Expr>
struct vector_expr : proto::extends <Expr, vector_expr <Expr>, vector_domain>
{
typedef proto::extends <Expr, vector_expr <Expr>, vector_domain> base_type;
vector_expr (Expr const &e) : base_type (e) {}
};
BOOST_PROTO_DEFINE_OPERATORS(is_vector, vector_domain)
Now the first thing I wanted to do was: check if all the vector terminals in the expression have the same size D. As a result, I got the following working code
template <class T>
struct vector_dim
{
typedef mpl::int_ <T::dimension> type;
};
template <class D1, class D2>
struct dim_combine
{
typedef mpl::int_ < -1 > type;
};
template <class D>
struct dim_combine <D, D>
{
typedef D type;
};
template <class D>
struct dim_combine <D, mpl::int_ <0> >
{
typedef D type;
};
template <class D>
struct dim_combine <mpl::int_ <0>, D>
{
typedef D type;
};
template <>
struct dim_combine <mpl::int_ <0>, mpl::int_<0> >
{
typedef mpl::int_<0> type;
};
struct vec_dim_check
: proto::or_ <
proto::when <
vector_terminal
, vector_dim<proto::_value>()
>
, proto::when <
scalar_terminal
, boost::mpl::int_<0>()
>
, proto::when <
proto::nary_expr<_, proto::vararg<_> >
, proto::fold<_, boost::mpl::int_<0>(), dim_combine<vec_dim_check, proto::_state>()>
>
>
{};
template <class E>
void check_dim (E const&)
{
typedef typename boost::result_of<vec_dim_check(E)>::type type;
BOOST_ASSERT(type::value == 3);
}
int main (int, char**)
{
vector <3,double> a,b,c;
check_dim (2*a+b/c);
return 0;
}
Question: since the dimension of arrays is already encoded in the expression, then it should be possible to detect an invalid combination already at compile time. You should even avoid creating a tree in the first place. How is this achieved?
Thanks in advance, best wishes