1

我正在尝试将惰性评估引入现有的代码项目。项目核心基本上由使用自定义类型的大量计算组成(它的作用类似于 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(...) 使我们的自定义类型成为非侵入式终端,但也不能进行延迟分配。

所以,我们的问题是我们是否可以实现我们想要的,或者我们是否必须更改现有代码以引入惰性评估?

感谢您的帮助,马蒂亚斯enter code here

4

1 回答 1

1

查看 Proto 文档中的惰性向量示例。有一个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<>包含分配节点的临时对象,然后将其丢弃。

于 2013-04-26T01:26:12.523 回答