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.
Finally I find a way to make an expression terminal of a primitive array to make a minimal EDSL for vector algebra. It can suppress superfluous copy of temporary objects when initializing terminal objects of expression templates. The key to the elimination of object copy is to put a primitive array in
Vectorclass, ' , to define a trait that returns true for thisVectorclass, and to useBOOST_PROTO_DEFINE_OPERATORS().Here's the source code :
I confirmed this code worked and the output was virtually same to that of the "Lazy Vector" example in Boost.Proto users' guide.
Although I am still not sure how things are going under the hood of Boost.Proto, it is quite fun to prototype an EDSL with it.