Domanda

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!

È stato utile?

Soluzione

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top