Boost.Proto: Как сделать выражение выражением из примитивного массива вместо std :: vector?

Сейчас я пытаюсь создать еще один мини-EDSL (встроенный предметно-ориентированный язык) для векторных выражений. На самом деле руководство пользователя Boost.Proto уже предоставило такой пример EDSL «Ленивый Вектор«, где векторные выражения сделаны из std::vector<T>, Но вместо этого я должен сделать эти выражения примитивных массивов. Потому что примитивная работа с массивами остается сердцем нескольких научных программ моделирования.

Таким образом, я добавил класс обертки массива, ArrayWrapper на этот код «Ленивый вектор» и заменил std::vector с ArrayWrapper, Этот измененный исходный код был успешно скомпилирован и связан. Но когда я запустил его, ядро ​​было сброшено.

Вот модифицированная версия исходного кода:

//  The original version of this file is :
//  "Lazy Vector: Controlling Operator Overloads"//  in Boost.Proto users' guide.
//  Copyright 2008 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0.
//
//  It was modified to try protofying a primitive array
//  on May 19 2015.

#include <vector>
#include <iostream>
#include <boost/mpl/int.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/context.hpp>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;template <typename T>
class ArrayWrapper {
private:
T* data;
size_t size_;

public:
typedef T value_type;

explicit ArrayWrapper(std::size_t size = 0, T const & value = T() ):
data( new T[size]), size_(size) {
for (std::size_t i = 0; i < size_; i++) data[i] = value;

}

~ArrayWrapper() {
std::cerr << "Now destructing an ArrayWrapper" << std::endl;
delete [] data;
}

std::size_t size() { return size_; }

T& operator[](std::size_t i) { return data[i]; }
T operator[](std::size_t i) const { return data[i]; }
};template<typename Expr>
struct lazy_vector_expr;

// This grammar describes which lazy vector expressions
// are allowed; namely, vector terminals and addition
// and subtraction of lazy vector expressions.
struct LazyVectorGrammar
: proto::or_<
proto::terminal< ArrayWrapper<_> >
, proto::plus< LazyVectorGrammar, LazyVectorGrammar >
, proto::minus< LazyVectorGrammar, LazyVectorGrammar >
>
{};

// Tell proto that in the lazy_vector_domain, all
// expressions should be wrapped in laxy_vector_expr<>
// and must conform to the lazy vector grammar.
struct lazy_vector_domain
: proto::domain<proto::generator<lazy_vector_expr>, LazyVectorGrammar>
{};

// Here is an evaluation context that indexes into a lazy vector
// expression, and combines the result.
template<typename Size = std::size_t>
struct lazy_subscript_context
{
lazy_subscript_context(Size subscript)
: subscript_(subscript)
{}

// Use default_eval for all the operations ...
template<typename Expr, typename Tag = typename Expr::proto_tag>
struct eval
: proto::default_eval<Expr, lazy_subscript_context>
{};

// ... except for terminals, which we index with our subscript
template<typename Expr>
struct eval<Expr, proto::tag::terminal>
{
typedef typename proto::result_of::value<Expr>::type::value_type result_type;

result_type operator ()( Expr const & expr, lazy_subscript_context & ctx ) const
{
return proto::value( expr )[ ctx.subscript_ ];
}
};

Size subscript_;
};

// Here is the domain-specific expression wrapper, which overrides
// operator [] to evaluate the expression using the lazy_subscript_context.
template<typename Expr>
struct lazy_vector_expr
: proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain>
{
lazy_vector_expr( Expr const & expr = Expr() )
: lazy_vector_expr::proto_extends( expr )
{}

// Use the lazy_subscript_context<> to implement subscripting
// of a lazy vector expression tree.
template< typename Size >
typename proto::result_of::eval< Expr, lazy_subscript_context<Size> >::type
operator []( Size subscript ) const
{
lazy_subscript_context<Size> ctx(subscript);
return proto::eval(*this, ctx);
}
};

// Here is our lazy_vector terminal, implemented in terms of lazy_vector_expr
template< typename T >
struct lazy_vector
: lazy_vector_expr< typename proto::terminal< ArrayWrapper<T> >::type >
{
typedef typename proto::terminal< ArrayWrapper<T> >::type expr_type;

lazy_vector( std::size_t size = 0, T const & value = T() )
: lazy_vector_expr<expr_type>( expr_type::make( ArrayWrapper<T>(size, value) ) )
{}

// Here we define a += operator for lazy vector terminals that
// takes a lazy vector expression and indexes it. expr[i] here
// uses lazy_subscript_context<> under the covers.
template< typename Expr >
lazy_vector & operator += (Expr const & expr)
{
std::size_t size = proto::value(*this).size();
for(std::size_t i = 0; i < size; ++i)
{
proto::value(*this)[i] += expr[i];
}
return *this;
}
};

int main()
{
// lazy_vectors with 4 elements each.
lazy_vector< double > v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 );

// Add two vectors lazily and get the 2nd element.
double d1 = ( v2 + v3 )[ 2 ];   // Look ma, no temporaries!
std::cout << d1 << std::endl;

// Subtract two vectors and add the result to a third vector.
v1 += v2 - v3;                  // Still no temporaries!
std::cout << '{' << v1[0] << ',' << v1[1]
<< ',' << v1[2] << ',' << v1[3] << '}' << std::endl;

// This expression is disallowed because it does not conform
// to the LazyVectorGrammar
//(v2 + v3) += v1;

return 0;
}

Я предполагаю, что мой класс-обертка массива имеет все необходимые функции-члены, которые нужны остальной части программы «Ленивый вектор». И я думаю, что интерфейс этих функций-членов такой же, как и у std::vector функции-члены, которые использует оригинальная программа «Lazy Vector».

Возможно, я скучаю по некоторым важным моментам. Но как это решить? (Как я должен сделать proto::terminal<T> объекты с примитивными массивами?) Буду очень признателен, если вы дадите мне совет или подсказку.

2

Решение

Наконец, я нахожу способ сделать терминальное выражение для примитивного массива минимальным EDSL для векторной алгебры. Это может подавить лишнюю копию временных объектов при инициализации терминальных объектов шаблонов выражений. Ключом к устранению копирования объекта является помещение примитивного массива в Vector class, ‘, чтобы определить черту, которая возвращает true для этого Vector класс, и использовать BOOST_PROTO_DEFINE_OPERATORS(),

Вот исходный код:

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

namespace mpl = boost::mpl;
namespace proto = boost::proto;

// This grammar describes which vector expressions
// are allowed; namely, vector terminals and addition
// and subtraction of vector expressions.
struct VecGrammar : proto::or_<
proto::terminal< proto::_ >,
proto::plus< VecGrammar, VecGrammar>,
proto::minus< VecGrammar, VecGrammar>
> {};// The above grammar is associated with this domain.
template<typename Expr> struct VecExpr;
struct VecDomain
: proto::domain<proto::generator<VecExpr>, VecGrammar> {};//
// Context for evaluating an element of matrix expressions
//
struct SubscriptCntxt
: proto::callable_context<const SubscriptCntxt> {
typedef double result_type;

int index;
SubscriptCntxt(int index_) :  index(index_) {}

// matrix element
template<typename Vector>
double operator()(proto::tag::terminal, const Vector& vec) const {
return vec[index];
}

// addition of vector expression terms
template<typename E1, typename E2>
double operator()(proto::tag::plus, const E1& e1, const E2& e2) const {
return proto::eval(e1, *this) + proto::eval(e2, *this);
}

// substraction of vector expression terms
template<typename E1, typename E2>
double operator()(proto::tag::minus, const E1& e1, const E2& e2) const {
return proto::eval(e1, *this) - proto::eval(e2, *this);
}
};//
// Vector Expression Templates
//
template<typename Expr>
struct VecExpr
: proto::extends<Expr, VecExpr<Expr>, VecDomain> {
explicit VecExpr(const Expr& e)
: proto::extends<Expr, VecExpr<Expr>, VecDomain>(e) {
}

// Use a SubscriptCntxt instance to implement subscripting
// of a vector expression tree.
typename proto::result_of::eval< Expr, SubscriptCntxt>::type
operator [](int i) const {
const SubscriptCntxt ctx(i);
return proto::eval(*this, ctx);
}
};

//
// Matrix data are stored in an heap array.
//
class Vector {
private:
int sz;
double* data;

public:
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;
std::cout << "Created" << std::endl;
}
Vector(const Vector& vec) :
sz( vec.sz), data( new double[sz] ) {
for (int i = 0; i < sz; i++) data[i] = vec.data[i];
std::cout << "Copied" << std::endl;
}

~Vector() {
delete [] data;
std::cout << "Deleted" << std::endl;
}

// accesing to a vector element
double& operator[](int i) { return data[i]; }
const double& operator[](int i) const { return data[i]; }

// assigning the lhs of a vector expression into this matrix
template<typename Expr>
Vector& operator=( const Expr& expr ) {
for(int i=0; i < sz; ++i) {
// evaluating the i'th element of a matrix expression
const SubscriptCntxt ctx(i);
data[i] = proto::eval(proto::as_expr<VecDomain>(expr), ctx);
}
return *this;
}

// assigning and adding the lhs of a vector expression into this matrix
template<typename Expr>
Vector& operator+=( const Expr& expr ) {
for(int i=0; i < sz; ++i) {
// evaluating the (i,j) element of a matrix expression
const SubscriptCntxt ctx(i);
data[i] += proto::eval(proto::as_expr<VecDomain>(expr), ctx);
}
return *this;
}
};// Define a trait for detecting vector terminals, to be used
// by the BOOST_PROTO_DEFINE_OPERATORS macro below.
template<typename> struct IsVector : mpl::false_ {};
template<> struct IsVector<Vector> : mpl::true_  {};namespace VectorOps {
// This defines all the overloads to make expressions involving
// Vector objects to build expression templates.
BOOST_PROTO_DEFINE_OPERATORS(IsVector, VecDomain)
}

int main()
{
using namespace VectorOps;

// lazy_vectors with 4 elements each.
Vector v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 );

// Add two vectors lazily and get the 2nd element.
double d1 = ( v2 + v3 )[ 2 ];   // Look ma, no temporaries!
std::cout << d1 << std::endl;

// Subtract two vectors and add the result to a third vector.
v1 += v2 - v3;                  // Still no temporaries!
std::cout << '{' << v1[0] << ',' << v1[1]
<< ',' << v1[2] << ',' << v1[3] << '}' << std::endl;

// This expression is disallowed because it does not conform
// to the LazyVectorGrammar
//(v2 + v3) += v1;

return 0;
}

Я подтвердил, что этот код работает, и результат был практически таким же, как в примере «Ленивый вектор» в руководстве пользователя Boost.Proto.

Хотя я до сих пор не уверен, как обстоят дела с Boost.Proto, довольно забавно создать на нем прототип EDSL.

2

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

Я надеюсь, что мог частично решить проблему в своем вопросе. После добавления конструктора копирования в класс ArrayWrapper измененная версия примера «Lazy Vector» в руководстве пользователя Boost.Proto работает без ошибок.

Вот исходный код:

    //  The original version of this file is :
//  "Lazy Vector: Controlling Operator Overloads"//  in Boost.Proto users' guide.
//  Copyright 2008 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0.
//
//  It was modified to try protofying a primitive array
//  on May 20 2015.

#include <vector>
#include <iostream>
#include <boost/mpl/int.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/context.hpp>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;

template <typename T>
class ArrayWrapper {
private:
T* data;
size_t size_;

public:
typedef T value_type;

explicit ArrayWrapper(std::size_t size = 0, T const & value = T() ):
data( new T[size]), size_(size) {
for (std::size_t i = 0; i < size_; i++) data[i] = value;

}
ArrayWrapper(const ArrayWrapper<T>& wrapper):
data( new T[ wrapper.size_] ), size_(wrapper.size_) {
for (std::size_t i = 0; i < size_; i++) data[i] = wrapper.data[i];
}~ArrayWrapper() {
std::cerr << "Now destructing an ArrayWrapper" << std::endl;
delete [] data;
}

std::size_t size() {
return size_;
}

T& operator[](std::size_t i) { return data[i]; }
T operator[](std::size_t i) const { return data[i]; }
};template<typename Expr>
struct lazy_vector_expr;

// This grammar describes which lazy vector expressions
// are allowed; namely, vector terminals and addition
// and subtraction of lazy vector expressions.
struct LazyVectorGrammar
: proto::or_<
proto::terminal< ArrayWrapper<_> >
, proto::plus< LazyVectorGrammar, LazyVectorGrammar >
, proto::minus< LazyVectorGrammar, LazyVectorGrammar >
>
{};

// Tell proto that in the lazy_vector_domain, all
// expressions should be wrapped in laxy_vector_expr<>
// and must conform to the lazy vector grammar.
struct lazy_vector_domain
: proto::domain<proto::generator<lazy_vector_expr>, LazyVectorGrammar>
{};

// Here is an evaluation context that indexes into a lazy vector
// expression, and combines the result.
template<typename Size = std::size_t>
struct lazy_subscript_context
{
lazy_subscript_context(Size subscript)
: subscript_(subscript)
{}

// Use default_eval for all the operations ...
template<typename Expr, typename Tag = typename Expr::proto_tag>
struct eval
: proto::default_eval<Expr, lazy_subscript_context>
{};

// ... except for terminals, which we index with our subscript
template<typename Expr>
struct eval<Expr, proto::tag::terminal>
{
typedef typename proto::result_of::value<Expr>::type::value_type result_type;

result_type operator ()( Expr const & expr, lazy_subscript_context & ctx ) const
{
return proto::value( expr )[ ctx.subscript_ ];
}
};

Size subscript_;
};

// Here is the domain-specific expression wrapper, which overrides
// operator [] to evaluate the expression using the lazy_subscript_context.
template<typename Expr>
struct lazy_vector_expr
: proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain>
{
lazy_vector_expr( Expr const & expr = Expr() )
: lazy_vector_expr::proto_extends( expr )
{}

// Use the lazy_subscript_context<> to implement subscripting
// of a lazy vector expression tree.
template< typename Size >
typename proto::result_of::eval< Expr, lazy_subscript_context<Size> >::type
operator []( Size subscript ) const
{
lazy_subscript_context<Size> ctx(subscript);
return proto::eval(*this, ctx);
}
};

// Here is our lazy_vector terminal, implemented in terms of lazy_vector_expr
template< typename T >
struct lazy_vector
: lazy_vector_expr< typename proto::terminal< ArrayWrapper<T> >::type >
{
typedef typename proto::terminal< ArrayWrapper<T> >::type expr_type;

lazy_vector( std::size_t size = 0, T const & value = T() )
: lazy_vector_expr<expr_type>( expr_type::make( ArrayWrapper<T>(size, value) ) )
{}

// Here we define a += operator for lazy vector terminals that
// takes a lazy vector expression and indexes it. expr[i] here
// uses lazy_subscript_context<> under the covers.
template< typename Expr >
lazy_vector & operator += (Expr const & expr)
{
std::size_t size = proto::value(*this).size();
for(std::size_t i = 0; i < size; ++i)
{
proto::value(*this)[i] += expr[i];
}
return *this;
}
};

int main()
{
// lazy_vectors with 4 elements each.
lazy_vector< double > v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 );

// Add two vectors lazily and get the 2nd element.
double d1 = ( v2 + v3 )[ 2 ];   // Look ma, no temporaries!
std::cout << d1 << std::endl;

// Subtract two vectors and add the result to a third vector.
v1 += v2 - v3;                  // Still no temporaries!
std::cout << '{' << v1[0] << ',' << v1[1]
<< ',' << v1[2] << ',' << v1[3] << '}' << std::endl;

// This expression is disallowed because it does not conform
// to the LazyVectorGrammar
//(v2 + v3) += v1;

return 0;
}

Но я не уверен, почему конструктор копирования по умолчанию ArrayWrapper вызвал дамп ядра до того, как я определил конструктор копирования. Может быть, конструктор копирования вызывается proto::expr< proto::tag::terminal, proto::term< ArrayWrapper<T>>> класс, когда expr_type::make( ArrayWrapper<T>(size, value) ) ) в lazy_vector конструктор класса инициализирует член данных expr_type класс (см. краткий обзор proto :: expr). Как вы видите в определении lazy_vector учебный класс, expr_type определяется как proto::expr< proto::tag::terminal, proto::term< ArrayWrapper<T>>> посредством typedef так что тип его члена данных, proto_childN, становится ArrayWrapper<T>,

Кроме того, остающаяся проблема заключается в том, что эти операции копирования ArrayWrapper<T> объекты замедляют программу, в отличие от назначения шаблонов выражений. Поэтому я должен признать, что мой ответ недостаточно хорош. Я пытаюсь найти лучший ответ …

1

По вопросам рекламы [email protected]