1

Suppose you'd want to implement a simple EDSL (Embedded Domain Specific Language) with Boost.proto with the following requirements:

  • Custom class 'Vector' as terminal
  • Classes derived from 'Vector' are working terminals too, e.g. Vector10

Reading the manual of Boost.proto it seems the closest related example to this would be the 'Vector: Adapting a Non-Proto Terminal Type' example.

Modifications I did to that example:

  • Added 'Vector' class
  • Protofied Vector instead of std::vector

Here the code (compiles):

#include <vector>
#include <iostream>
#include <stdexcept>
#include <boost/mpl/bool.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/debug.hpp>
#include <boost/proto/context.hpp>
#include <boost/utility/enable_if.hpp>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;

class Vector;

template<typename Expr>
struct VectorExpr;

struct VectorSubscriptCtx
{
    VectorSubscriptCtx(std::size_t i)
      : i_(i)
    {}

    template<typename Expr, typename EnableIf = void>
    struct eval
      : proto::default_eval<Expr, VectorSubscriptCtx const>
    {};

    template<typename Expr>
    struct eval<
        Expr
      , typename boost::enable_if<
            proto::matches<Expr, proto::terminal< Vector > >
        >::type
    >
    {
        typedef typename proto::result_of::value<Expr>::type::value_type result_type;

        result_type operator ()(Expr &expr, VectorSubscriptCtx const &ctx) const
        {
            return proto::value(expr)[ctx.i_];
        }
    };

    std::size_t i_;
};




struct VectorGrammar : proto::or_<
    proto::terminal< proto::_ >,
    proto::plus< VectorGrammar, VectorGrammar>,
    proto::minus< VectorGrammar, VectorGrammar>
> {};

struct VectorDomain
  : proto::domain<proto::generator<VectorExpr>, VectorGrammar>
{};



template<typename Expr>
struct VectorExpr
  : proto::extends<Expr, VectorExpr<Expr>, VectorDomain>
{
    explicit VectorExpr(Expr const &expr)
      : proto::extends<Expr, VectorExpr<Expr>, VectorDomain>(expr)
    {}

    typename proto::result_of::eval<Expr const, VectorSubscriptCtx const>::type
    operator []( std::size_t i ) const
    {
        VectorSubscriptCtx const ctx(i);
        return proto::eval(*this, ctx);
    }
};




class Vector {
private:
  int sz;
  double* data;

public:
  typedef double value_type;

  explicit Vector(int sz_ = 1, double iniVal = 0.0) :
    sz( sz_), data( new double[sz] ) {
    for (int i = 0; i < sz; i++) data[i] = iniVal;        
  }
  Vector(const Vector& vec) :
    sz( vec.sz), data( new double[sz] ) {
    for (int i = 0; i < sz; i++) data[i] = vec.data[i];
  }

  size_t size() const {return sz; }

  ~Vector() {
    delete [] data;
  }

  double& operator[](int i) { return data[i]; }
  const double& operator[](int i) const { return data[i]; }
};



class Vector10: public Vector 
{
public:
  Vector10() : Vector(10,0.0) {}
};




template<typename T>
struct IsVector
  : mpl::false_
{};


template<>
struct IsVector< Vector >
  : mpl::true_
{};



namespace VectorOps
{
    BOOST_PROTO_DEFINE_OPERATORS(IsVector, VectorDomain)

    typedef VectorSubscriptCtx const CVectorSubscriptCtx;

    template<typename Expr>
    Vector &assign(Vector &arr, Expr const &expr)
    {
        for(std::size_t i = 0; i < arr.size(); ++i)
        {
            arr[i] = proto::as_expr<VectorDomain>(expr)[i];
        }
        return arr;
    }
}



int main()
{
    using namespace VectorOps;

    Vector a,b,c,d;

    VectorOps::assign(d, a + b );
}

There's already the derived class Vector10 defined. Now - using that one instead of Vector

int main()
{
    using namespace VectorOps;

    Vector10 a,b,c,d;

    VectorOps::assign(d, a + b );
}

results in a compilation error

vector_proto_baseclass.cc:168:28: error: no match for ‘operator+’ (operand types are ‘Vector10’ and ‘Vector10’)

I believe that the operators for Vector are correctly defined in the namespace VectorOps but the ADL doesn't kick in for the derived class.

ritter
  • 7,447
  • 7
  • 51
  • 84

0 Answers0