使用表达式树非侵入式替换自定义类型

时间:2013-04-24 17:29:35

标签: c++ lazy-evaluation boost-proto

我正在尝试将懒惰评估引入现有代码项目。项目核心基本上包括使用自定义类型的大量计算(它的作用类似于double,但在后台执行其他工作)。

我们的目标是使用boost proto引入一个懒惰的评估概念,以优化现有的表达式。

限制:

  • 无法触及现有的计算
  • 使用的类型由typedef定义,因此可以替换类型本身

我们尝试实现一个简单的概念验证,但没有按照需要管理代码。这是我们到目前为止所得到的:

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

我们的主要问题是我们无法定义一个既适用于文字又适用于表达式的类型。在此示例中,c是proto :: terminal类型的表达式,并忽略表达式的赋值。使用auto存储表达式时,它工作正常。此外,无法直接初始化。

如果我理解我们的问题,我们需要两种不同的表达式和文字类型,这是不可能的,因为我们只能改变现有的类型。

我们还研究了其他选项,比如使用BOOST_PROTO_DEFINE_OPERATORS(...)来使我们的自定义类型成为非侵入式终端,但也无法进行延迟分配。

所以,我们的问题是,如果我们能够实现我们想要的或者我们必须改变现有的代码来引入懒惰的评估吗?

感谢您的帮助, 的Matthias enter code here

1 个答案:

答案 0 :(得分:1)

查看Proto文档中的lazy vector example。有一个lazy_vector_expr模板用于延迟向量表达式,一个lazy_vector模板专门用于终端(从lazy_vector_expr继承其大部分实现)。 lazy_vector定义了一个+=运算符,它接受任意的惰性向量表达式,对它们进行求值并存储结果。

听起来像你在做类似的事情。但是,而不是+=,你正在寻找普通的任务和转换建设。因此,定义模板的构造函数和赋值运算符。像(未经测试的)......

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;

顺便提一下,当你使用BOOST_PROTO_EXTENDS_USING_ASSIGN时,你得到的是一个重载的赋值运算符,它构建了一个更大的表达式。所以这个:

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

...将会产生令人不快的效果,即创建一个包含赋值节点的临时MyDoubleExpr<>,然后将其丢弃。

HTH!