Here is my solution:
#include <vector>
#include <iostream>
#include <boost/proto/proto.hpp>
#include <boost/format.hpp>
namespace proto = boost::proto;
using proto::_;
typedef double real;
struct vector_grammar
: proto::or_<
proto::terminal< std::vector<real> >
, proto::plus< vector_grammar, vector_grammar >
, proto::minus< vector_grammar, vector_grammar >
, proto::multiplies< vector_grammar, vector_grammar >
, proto::divides< vector_grammar, vector_grammar >
>
{};
template<typename Expr>
struct vector_expr;
// Tell proto that in the vector_domain, all
// expressions should be wrapped in vector_expr<> and
// must conform to the vector_grammar
struct vector_domain
: proto::domain<
// use_basic_expr here instructs proto to use the stripped-
// down proto::basic_expr instead of proto::expr to reduce
// compile times. It's not necessary.
proto::use_basic_expr<proto::pod_generator<vector_expr> >
, vector_grammar
>
{};
struct vector_subscript_context
: proto::callable_context< vector_subscript_context const >
{
typedef real result_type;
explicit vector_subscript_context(std::ptrdiff_t i)
: i_(i)
{}
// Index array terminals with our subscript. Everything
// else will be handled by the default evaluation context.
real operator ()(proto::tag::terminal, std::vector<real> const &data) const
{
return data[this->i_];
}
private:
std::ptrdiff_t i_;
};
// Forward-declare an expression wrapper
template<typename Expr>
struct vector_expr
{
BOOST_PROTO_BASIC_EXTENDS(Expr, vector_expr<Expr>, vector_domain)
// Use the vector_subscript_context to implement subscripting
// of a vector_expr expression tree.
real operator []( std::ptrdiff_t i ) const
{
vector_subscript_context const ctx(i);
return proto::eval(*this, ctx);
}
};
template<typename = proto::is_proto_expr>
struct FPVector_
: vector_expr<
proto::basic_expr<
proto::tag::terminal
, proto::term< std::vector<real> >
>
>
{
explicit FPVector_(std::size_t n = 0)
{
proto::value(*this).resize(n);
}
real & operator[](std::ptrdiff_t i)
{
return proto::value(*this)[i];
}
real const & operator[](std::ptrdiff_t i) const
{
return proto::value(*this)[i];
}
template<typename Expr>
FPVector_ & operator=(vector_expr<Expr> const & that)
{
std::ptrdiff_t const size =
static_cast<std::ptrdiff_t>(proto::value(*this).size());
for(std::ptrdiff_t i = 0; i < size; ++i)
proto::value(*this)[i] = that[i];
return *this;
}
};
typedef FPVector_<> FPVector;
int main()
{
FPVector a(3), b(3), c(3);
for(std::ptrdiff_t i = 0; i < 3; ++i)
b[i] = c[i] = i;
a = b + c;
std::cout
<< boost::format("a = {%d, %d, %d}") % a[0] %a[1] %a[2]
<< std::endl;
}
The code you show has a number of problems:
// Forward-declare an expression wrapper
template<typename Expr>
struct vector_expr_wrapper; // line 13
class FPVector : vector_expr_wrapper< proto::terminal< FPVector >::type >
You can't inherit from an incomplete class template (vector_expr_wrapper
) from a (concrete) class (FPVector
). Also, your FPVector
type is trying to contain a copy of itself. proto::terminal
is a wrapper around an object type. Think of it as a stand-in for the thing itself, but with some extra Proto spice. Then inheriting from it or from something that inherits from it is not going to fly.
The only real trick in the code I posted is the use of proto::is_proto_expr
. This is a nasty hack to get ADL to kick in an find the operator overloads defined in the boost::proto
namespace. The thing is described in detail in the warning at the bottom of "The extends<> Expression Wrapper" section of proto's docs.