Boost logo

Proto :

Subject: [proto] Using a derived class as terminals in Boost.proto
From: Frank Winter (fwinter_at_[hidden])
Date: 2016-04-14 09:43:04


Hi all!

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 );
}

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.

Any idea how this can be fixed?

Thanks,
Frank


Proto list run by eric at boostpro.com