Now I am trying to make yet another mini-EDSL (embedded domain-specific language) for vector expressions. Actually Boost.Proto users' guide already provided such an EDSL example, "Lazy Vector", where vector expressions are made of std::vector<T>
. But I have to make those expressions of primitive arrays instead. Because primitive array operation is still the heart of several scientific simulation programs.
Thus I added an array wrapper class, ArrayWrapper
to that "Lazy Vector" code and replaced std::vector
with ArrayWrapper
. This modified source code was successfully compiled and linked. But when I ran it, core was dumped.
Here is the modified version of source code:
// 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;
}
I suppose my array wrapper class has all the necessary member functions which the rest of the "Lazy vector" program needs. And I think that the interface of those member functions are the same as that of the std::vector
member functions which the original "Lazy Vector" program uses.
Probably I miss some important points. But how to solve this? (How should I make proto::terminal<T>
objects with primitive arrays?) I'd be very grateful if you would give me advice or hints.