242 lines
7.2 KiB
C++
242 lines
7.2 KiB
C++
//[ Vector
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright 2008 Eric Niebler. Distributed under the Boost
|
|
// Software License, Version 1.0. (See accompanying file
|
|
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
// This is an example of using BOOST_PROTO_DEFINE_OPERATORS to Protofy
|
|
// expressions using std::vector<>, a non-proto type. It is a port of the
|
|
// Vector example from PETE (http://www.codesourcery.com/pooma/download.html).
|
|
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <boost/mpl/bool.hpp>
|
|
#include <boost/proto/core.hpp>
|
|
#include <boost/proto/debug.hpp>
|
|
#include <boost/proto/context.hpp>
|
|
#include <boost/utility/enable_if.hpp>
|
|
namespace mpl = boost::mpl;
|
|
namespace proto = boost::proto;
|
|
using proto::_;
|
|
|
|
template<typename Expr>
|
|
struct VectorExpr;
|
|
|
|
// Here is an evaluation context that indexes into a std::vector
|
|
// expression and combines the result.
|
|
struct VectorSubscriptCtx
|
|
{
|
|
VectorSubscriptCtx(std::size_t i)
|
|
: i_(i)
|
|
{}
|
|
|
|
// Unless this is a vector terminal, use the
|
|
// default evaluation context
|
|
template<typename Expr, typename EnableIf = void>
|
|
struct eval
|
|
: proto::default_eval<Expr, VectorSubscriptCtx const>
|
|
{};
|
|
|
|
// Index vector terminals with our subscript.
|
|
template<typename Expr>
|
|
struct eval<
|
|
Expr
|
|
, typename boost::enable_if<
|
|
proto::matches<Expr, proto::terminal<std::vector<_, _> > >
|
|
>::type
|
|
>
|
|
{
|
|
typedef typename proto::result_of::value<Expr>::type::value_type result_type;
|
|
|
|
result_type operator ()(Expr &expr, VectorSubscriptCtx const &ctx) const
|
|
{
|
|
return proto::value(expr)[ctx.i_];
|
|
}
|
|
};
|
|
|
|
std::size_t i_;
|
|
};
|
|
|
|
// Here is an evaluation context that verifies that all the
|
|
// vectors in an expression have the same size.
|
|
struct VectorSizeCtx
|
|
{
|
|
VectorSizeCtx(std::size_t size)
|
|
: size_(size)
|
|
{}
|
|
|
|
// Unless this is a vector terminal, use the
|
|
// null evaluation context
|
|
template<typename Expr, typename EnableIf = void>
|
|
struct eval
|
|
: proto::null_eval<Expr, VectorSizeCtx const>
|
|
{};
|
|
|
|
// Index array terminals with our subscript. Everything
|
|
// else will be handled by the default evaluation context.
|
|
template<typename Expr>
|
|
struct eval<
|
|
Expr
|
|
, typename boost::enable_if<
|
|
proto::matches<Expr, proto::terminal<std::vector<_, _> > >
|
|
>::type
|
|
>
|
|
{
|
|
typedef void result_type;
|
|
|
|
result_type operator ()(Expr &expr, VectorSizeCtx const &ctx) const
|
|
{
|
|
if(ctx.size_ != proto::value(expr).size())
|
|
{
|
|
throw std::runtime_error("LHS and RHS are not compatible");
|
|
}
|
|
}
|
|
};
|
|
|
|
std::size_t size_;
|
|
};
|
|
|
|
// A grammar which matches all the assignment operators,
|
|
// so we can easily disable them.
|
|
struct AssignOps
|
|
: proto::switch_<struct AssignOpsCases>
|
|
{};
|
|
|
|
// Here are the cases used by the switch_ above.
|
|
struct AssignOpsCases
|
|
{
|
|
template<typename Tag, int D = 0> struct case_ : proto::not_<_> {};
|
|
|
|
template<int D> struct case_< proto::tag::plus_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::minus_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::multiplies_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::divides_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::modulus_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::shift_left_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::shift_right_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::bitwise_and_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::bitwise_or_assign, D > : _ {};
|
|
template<int D> struct case_< proto::tag::bitwise_xor_assign, D > : _ {};
|
|
};
|
|
|
|
// A vector grammar is a terminal or some op that is not an
|
|
// assignment op. (Assignment will be handled specially.)
|
|
struct VectorGrammar
|
|
: proto::or_<
|
|
proto::terminal<_>
|
|
, proto::and_<proto::nary_expr<_, proto::vararg<VectorGrammar> >, proto::not_<AssignOps> >
|
|
>
|
|
{};
|
|
|
|
// Expressions in the vector domain will be wrapped in VectorExpr<>
|
|
// and must conform to the VectorGrammar
|
|
struct VectorDomain
|
|
: proto::domain<proto::generator<VectorExpr>, VectorGrammar>
|
|
{};
|
|
|
|
// Here is VectorExpr, which extends a proto expr type by
|
|
// giving it an operator [] which uses the VectorSubscriptCtx
|
|
// to evaluate an expression with a given index.
|
|
template<typename Expr>
|
|
struct VectorExpr
|
|
: proto::extends<Expr, VectorExpr<Expr>, VectorDomain>
|
|
{
|
|
explicit VectorExpr(Expr const &expr)
|
|
: proto::extends<Expr, VectorExpr<Expr>, VectorDomain>(expr)
|
|
{}
|
|
|
|
// Use the VectorSubscriptCtx to implement subscripting
|
|
// of a Vector expression tree.
|
|
typename proto::result_of::eval<Expr const, VectorSubscriptCtx const>::type
|
|
operator []( std::size_t i ) const
|
|
{
|
|
VectorSubscriptCtx const ctx(i);
|
|
return proto::eval(*this, ctx);
|
|
}
|
|
};
|
|
|
|
// Define a trait type for detecting vector terminals, to
|
|
// be used by the BOOST_PROTO_DEFINE_OPERATORS macro below.
|
|
template<typename T>
|
|
struct IsVector
|
|
: mpl::false_
|
|
{};
|
|
|
|
template<typename T, typename A>
|
|
struct IsVector<std::vector<T, A> >
|
|
: mpl::true_
|
|
{};
|
|
|
|
namespace VectorOps
|
|
{
|
|
// This defines all the overloads to make expressions involving
|
|
// std::vector to build expression templates.
|
|
BOOST_PROTO_DEFINE_OPERATORS(IsVector, VectorDomain)
|
|
|
|
typedef VectorSubscriptCtx const CVectorSubscriptCtx;
|
|
|
|
// Assign to a vector from some expression.
|
|
template<typename T, typename A, typename Expr>
|
|
std::vector<T, A> &assign(std::vector<T, A> &arr, Expr const &expr)
|
|
{
|
|
VectorSizeCtx const size(arr.size());
|
|
proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match
|
|
for(std::size_t i = 0; i < arr.size(); ++i)
|
|
{
|
|
arr[i] = proto::as_expr<VectorDomain>(expr)[i];
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
// Add-assign to a vector from some expression.
|
|
template<typename T, typename A, typename Expr>
|
|
std::vector<T, A> &operator +=(std::vector<T, A> &arr, Expr const &expr)
|
|
{
|
|
VectorSizeCtx const size(arr.size());
|
|
proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match
|
|
for(std::size_t i = 0; i < arr.size(); ++i)
|
|
{
|
|
arr[i] += proto::as_expr<VectorDomain>(expr)[i];
|
|
}
|
|
return arr;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
using namespace VectorOps;
|
|
|
|
int i;
|
|
const int n = 10;
|
|
std::vector<int> a,b,c,d;
|
|
std::vector<double> e(n);
|
|
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
a.push_back(i);
|
|
b.push_back(2*i);
|
|
c.push_back(3*i);
|
|
d.push_back(i);
|
|
}
|
|
|
|
VectorOps::assign(b, 2);
|
|
VectorOps::assign(d, a + b * c);
|
|
a += if_else(d < 30, b, c);
|
|
|
|
VectorOps::assign(e, c);
|
|
e += e - 4 / (c + 1);
|
|
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
std::cout
|
|
<< " a(" << i << ") = " << a[i]
|
|
<< " b(" << i << ") = " << b[i]
|
|
<< " c(" << i << ") = " << c[i]
|
|
<< " d(" << i << ") = " << d[i]
|
|
<< " e(" << i << ") = " << e[i]
|
|
<< std::endl;
|
|
}
|
|
}
|
|
//]
|