Domanda

I'm trying to introduce lazy evaluation into an existing code project. The project core basically consists of a large amount of calculations using a custom type (it acts like a double but does additional work in the background).

Our goal is to introduce a lazy evaluation concept using boost proto in Order to optimize existing expressions.

Restrictions:

  • The existing calculations can't be touched
  • The type used is defined by a typedef, so replacing the type itself is possible

We tried to implement a simple proof of concept but didn't manage the code to behave as desired. This is what we got so far:

#include <boost/proto/proto.hpp>
#include <complex>
#include <iostream>

using namespace std;
using namespace boost;
using namespace boost::proto;

// The custom implemented Type
typedef std::complex<double> my_type;

// The Basic expression wrapper
template< typename Expr = proto::terminal< my_type >::type >
struct MyDoubleExpr
  : proto::extends< Expr, MyDoubleExpr< Expr >, proto::default_domain >
{
    typedef
        proto::extends< Expr, MyDoubleExpr< Expr >, proto::default_domain >
    base_type;

    MyDoubleExpr( Expr const &expr = Expr() )
      : base_type( expr )
    {}

  // Overloading of all Constructors supported by the custom type
  typedef typename proto::terminal< my_type >::type expr_type;
  MyDoubleExpr( double const &d)
    : base_type( expr_type::make(my_type(d)))
    {}

  // Lazy assignment is desired
    BOOST_PROTO_EXTENDS_USING_ASSIGN(MyDoubleExpr)

};

// unintrusively replace the existing type with
// the expression template
typedef MyDoubleExpr<> replaced_type;


int main() {

  replaced_type a = 2.0, b = 1.5;

  proto::default_context ctx;

  // The replaced type is created as template specialisation
  // proto::terminal< my_type >::type -> cannot store expressions
  replaced_type c;
  c = (a + b) * 2.0;
  std::cout << "c: " << proto::eval(c,ctx) << endl << endl;
  proto::display_expr(c);

  // Initialisation does not work directly ?
  //replaced_type d = a+b;

  // using auto works fine, so the expression basically works
  auto e = (a + b) * 2.0;
  std::cout << "e: " << proto::eval(e,ctx) << endl;
  proto::display_expr(e);

  getchar();
  return 0;
}

Our main problem is that we can't define a type which works with both literals and expressions alike. In this example, c is a Expression of type proto::terminal and ignores the assignment of the expression. When using auto to store the expression, it works fine. Also, direct initialization is not possible.

If i understand our problem correctly we would require two different types for expressions and literals, which is not possible because we can only change the existing type.

We also looked into other options, like using BOOST_PROTO_DEFINE_OPERATORS(...) to make our custom type a terminal non-intrusively, but lazy assignment is not possible either.

So, our question is if we can achieve what we want or do we have to change the existing code to introduce lazy evaluation?

Thanks for your help, Matthiasenter code here

È stato utile?

Soluzione

Have a look at the lazy vector example from Proto's docs. There is a lazy_vector_expr template for lazy vector expressions, and a lazy_vector template specifically for terminals (which inherits most of its implementation from lazy_vector_expr). lazy_vector defines a += operator that accepts arbitrary lazy-vector expressions, evaluates them, and stores the result.

It sounds to me like you're doing something similar. But instead of +=, you are looking for ordinary assignment and converting construction. So define a template'd constructor and assignment operator. Something like (untested)...

template<typename XXX = proto::is_proto_expr> // hack, needed to for ADL
struct replaced_type_t
  : MyDoubleExpr< proto::terminal<double>::type > 
{
    replaced_type_t(double d = 0.0)
      : MyDoubleExpr< proto::terminal<double>::type >(
            proto::terminal<double>::type::make(d)
        )
    {}
    // Converting constructor
    template<typename OtherExpr>
    replaced_type_t(MyDoubleExpr<OtherExpr> const & e)
    {
        // Evaluate e, assign to *this
    }
    // Converting assignment
    template<typename OtherExpr>
    replaced_type_t& operator=(MyDoubleExpr<OtherExpr> const & e)
    {
        // Evaluate e, assign to *this
    }
};

typedef replaced_type_t<> replaced_type;

Incidentally, when you used BOOST_PROTO_EXTENDS_USING_ASSIGN, what you got was an overloaded assignment operator that building a bigger expression. So this:

replaced_type c;
c = (a + b) * 2.0;

... is going to have the unexciting effect of creating a temporary MyDoubleExpr<> containing an assignment node, and then throwing it away.

HTH!

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