1

In order to familiarize myself with Boost.Proto I am trying to build yet another expression template library for floating-point vectors of fixed but arbitrary size by adapting the TArray example from the user's guide. The first thing I do is to define my vector class:

typedef double real;

// Forward-declare an expression wrapper
template<typename Expr>
struct vector_expr_wrapper; // line 13

class FPVector : vector_expr_wrapper< proto::terminal< FPVector >::type > { // line 16
public:
    FPVector() : numElements(0), elements(0) {}
    FPVector(size_t n) : numElements(n), elements(new real[n]) {}
    ~FPVector() { delete[] elements; }

    real& operator[](size_t i) { return elements[i]; }

    template<typename Expr>
    FPVector const& operator=(vector_expr_wrapper<Expr> vec_expr) {
        for(size_t i=0; i<numElements; i++) {
            elements[i] = vec_expr[i];
        }
        return *this;
    }

private:
    size_t numElements;
    real * elements;
};

vector_expr_wrapper also overloads its operator[] to evaluate itself with a vector_context derived from proto::callable_context that returns vector[index] for FPVector terminals.

When I compile my code and call it with a very simple statement (a = b + c;) I get the error message:

../main.cpp:16:18: error: invalid use of incomplete type ‘struct vector_expr_wrapper<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<FPVector>, 0l> >’
../main.cpp:13:8: error: declaration of ‘struct vector_expr_wrapper<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<FPVector>, 0l> >’
../main.cpp: In function ‘int main()’:
../main.cpp:121:8: error: no match for ‘operator+’ in ‘b + c’

and then g++ lists the possible candidates stuff... What I understand from that is that I have to give the whole definition of vector_expr_wrapper before definig FPVector but I cannot do so because everything else in vector_expr_wrapper depends on FPVector (the grammar, the evaluation context...)

How can I solve this (i.e. how should I layout my classes)?

The TArray example circuments this problem -- I guess -- by defining their array class very late and by specifying its type with int[3] before, which I think I cannot reproduce in my context.

Thank you very much for your help!

o.svensson
  • 329
  • 2
  • 9

1 Answers1

2

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.

Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • Thanks Eric. But what if I did _not_ want to implement FPVector using `std::vector`? Would this not lead to almost the same dependency issue I stated in my question? – o.svensson Aug 22 '13 at 16:58
  • No, it wouldn't. You could replace `std::vector` with `real*` everywhere and then dynamically manage the memory yourself. I don't recommend it, though; that is exactly the purpose of `std::vector`, and you can get a `real*` from it any time you want. – Eric Niebler Aug 22 '13 at 19:00
  • Maybe I should not have used this specific example. What I want to get at, is the scenario that we have some class that wants to do _something_ with Proto expressions involving itself. Then I think I would have to replace `std::vector` with the class itself and this would -- as far as I can see -- result in the named dependency issue. Or is there no issue at all and I am just too confused to realize? (Which would probably be a safe bet because this meta-programming stuff really twists my brain ;) ) – o.svensson Aug 23 '13 at 09:14
  • "we have some class that wants to do _something_ with Proto expressions involving itself." I'm afraid I don't know what you mean. Can you explain? – Eric Niebler Aug 23 '13 at 16:14
  • I think it is easier if I try to answer my question myself and you say whether I got it right or wrong: If I want to define a class that can be used in Proto expressions, I first implement it without any connection to Proto (the implementer of std::vector does not think about Proto expressions). Then I write my Proto grammar, expression wrapper, and domain based on this class. After that, I can define a new class that inherits from my base class just like in your example. In this last class I finally define my Proto specific behavior (assignment operator for expressions etc). Correct? – o.svensson Aug 27 '13 at 11:42
  • That sounds right to me. Some Proto-specific behavior can go into the expression wrapper though (see `vector_expr::operator[]` above). – Eric Niebler Aug 27 '13 at 19:05