Ненавязчивая замена пользовательского типа на дерево выражений

Я пытаюсь ввести ленивую оценку в существующий проект кода. Ядро проекта в основном состоит из большого количества вычислений, использующих пользовательский тип (он действует как двойной, но выполняет дополнительную работу в фоновом режиме).

Наша цель — представить концепцию отложенной оценки с использованием boost proto in Order для оптимизации существующих выражений.

Ограничения:

  • Существующие расчеты не могут быть затронуты
  • Используемый тип определяется 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 и игнорирует присвоение выражения. При использовании авто для сохранения выражения все работает нормально. Также прямая инициализация невозможна.

Если я правильно понимаю нашу проблему, нам потребуются два разных типа для выражений и литералов, что невозможно, поскольку мы можем только изменить существующий тип.

Мы также рассмотрели другие варианты, такие как использование BOOST_PROTO_DEFINE_OPERATORS (…), чтобы сделать наш пользовательский тип терминалом ненавязчивым, но ленивое присваивание также невозможно.

Итак, наш вопрос: можем ли мы достичь того, чего хотим, или нам нужно изменить существующий код, чтобы ввести ленивую оценку?

Спасибо за вашу помощь,
Матиасenter code here

1

Решение

Посмотрите на пример ленивого вектора из документов Прото. Eсть 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<> содержащий узел назначения, а затем выбрасывая его.

НТН!

1

Другие решения

Других решений пока нет …