376 lines
11 KiB
C++
376 lines
11 KiB
C++
//[ Mixed
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// 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<> and std::list, non-proto types. It is a port
|
|
// of the Mixed example from PETE.
|
|
// (http://www.codesourcery.com/pooma/download.html).
|
|
|
|
#include <list>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <complex>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <boost/proto/core.hpp>
|
|
#include <boost/proto/debug.hpp>
|
|
#include <boost/proto/context.hpp>
|
|
#include <boost/proto/transform.hpp>
|
|
#include <boost/utility/enable_if.hpp>
|
|
#include <boost/typeof/std/list.hpp>
|
|
#include <boost/typeof/std/vector.hpp>
|
|
#include <boost/typeof/std/complex.hpp>
|
|
#include <boost/type_traits/remove_reference.hpp>
|
|
namespace proto = boost::proto;
|
|
namespace mpl = boost::mpl;
|
|
using proto::_;
|
|
|
|
template<typename Expr>
|
|
struct MixedExpr;
|
|
|
|
template<typename Iter>
|
|
struct iterator_wrapper
|
|
{
|
|
typedef Iter iterator;
|
|
|
|
explicit iterator_wrapper(Iter iter)
|
|
: it(iter)
|
|
{}
|
|
|
|
mutable Iter it;
|
|
};
|
|
|
|
struct begin : proto::callable
|
|
{
|
|
template<class Sig>
|
|
struct result;
|
|
|
|
template<class This, class Cont>
|
|
struct result<This(Cont)>
|
|
: proto::result_of::as_expr<
|
|
iterator_wrapper<typename boost::remove_reference<Cont>::type::const_iterator>
|
|
>
|
|
{};
|
|
|
|
template<typename Cont>
|
|
typename result<begin(Cont const &)>::type
|
|
operator ()(Cont const &cont) const
|
|
{
|
|
iterator_wrapper<typename Cont::const_iterator> it(cont.begin());
|
|
return proto::as_expr(it);
|
|
}
|
|
};
|
|
|
|
// Here is a grammar that replaces vector and list terminals with their
|
|
// begin iterators
|
|
struct Begin
|
|
: proto::or_<
|
|
proto::when< proto::terminal< std::vector<_, _> >, begin(proto::_value) >
|
|
, proto::when< proto::terminal< std::list<_, _> >, begin(proto::_value) >
|
|
, proto::when< proto::terminal<_> >
|
|
, proto::when< proto::nary_expr<_, proto::vararg<Begin> > >
|
|
>
|
|
{};
|
|
|
|
// Here is an evaluation context that dereferences iterator
|
|
// terminals.
|
|
struct DereferenceCtx
|
|
{
|
|
// Unless this is an iterator terminal, use the
|
|
// default evaluation context
|
|
template<typename Expr, typename EnableIf = void>
|
|
struct eval
|
|
: proto::default_eval<Expr, DereferenceCtx const>
|
|
{};
|
|
|
|
// Dereference iterator terminals.
|
|
template<typename Expr>
|
|
struct eval<
|
|
Expr
|
|
, typename boost::enable_if<
|
|
proto::matches<Expr, proto::terminal<iterator_wrapper<_> > >
|
|
>::type
|
|
>
|
|
{
|
|
typedef typename proto::result_of::value<Expr>::type IteratorWrapper;
|
|
typedef typename IteratorWrapper::iterator iterator;
|
|
typedef typename std::iterator_traits<iterator>::reference result_type;
|
|
|
|
result_type operator ()(Expr &expr, DereferenceCtx const &) const
|
|
{
|
|
return *proto::value(expr).it;
|
|
}
|
|
};
|
|
};
|
|
|
|
// Here is an evaluation context that increments iterator
|
|
// terminals.
|
|
struct IncrementCtx
|
|
{
|
|
// Unless this is an iterator terminal, use the
|
|
// default evaluation context
|
|
template<typename Expr, typename EnableIf = void>
|
|
struct eval
|
|
: proto::null_eval<Expr, IncrementCtx const>
|
|
{};
|
|
|
|
// advance iterator terminals.
|
|
template<typename Expr>
|
|
struct eval<
|
|
Expr
|
|
, typename boost::enable_if<
|
|
proto::matches<Expr, proto::terminal<iterator_wrapper<_> > >
|
|
>::type
|
|
>
|
|
{
|
|
typedef void result_type;
|
|
|
|
result_type operator ()(Expr &expr, IncrementCtx const &) const
|
|
{
|
|
++proto::value(expr).it;
|
|
}
|
|
};
|
|
};
|
|
|
|
// 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 > : _ {};
|
|
};
|
|
|
|
// An expression conforms to the MixedGrammar if it is a terminal or some
|
|
// op that is not an assignment op. (Assignment will be handled specially.)
|
|
struct MixedGrammar
|
|
: proto::or_<
|
|
proto::terminal<_>
|
|
, proto::and_<
|
|
proto::nary_expr<_, proto::vararg<MixedGrammar> >
|
|
, proto::not_<AssignOps>
|
|
>
|
|
>
|
|
{};
|
|
|
|
// Expressions in the MixedDomain will be wrapped in MixedExpr<>
|
|
// and must conform to the MixedGrammar
|
|
struct MixedDomain
|
|
: proto::domain<proto::generator<MixedExpr>, MixedGrammar>
|
|
{};
|
|
|
|
// Here is MixedExpr, a wrapper for expression types in the MixedDomain.
|
|
template<typename Expr>
|
|
struct MixedExpr
|
|
: proto::extends<Expr, MixedExpr<Expr>, MixedDomain>
|
|
{
|
|
explicit MixedExpr(Expr const &expr)
|
|
: MixedExpr::proto_extends(expr)
|
|
{}
|
|
private:
|
|
// hide this:
|
|
using proto::extends<Expr, MixedExpr<Expr>, MixedDomain>::operator [];
|
|
};
|
|
|
|
// Define a trait type for detecting vector and list terminals, to
|
|
// be used by the BOOST_PROTO_DEFINE_OPERATORS macro below.
|
|
template<typename T>
|
|
struct IsMixed
|
|
: mpl::false_
|
|
{};
|
|
|
|
template<typename T, typename A>
|
|
struct IsMixed<std::list<T, A> >
|
|
: mpl::true_
|
|
{};
|
|
|
|
template<typename T, typename A>
|
|
struct IsMixed<std::vector<T, A> >
|
|
: mpl::true_
|
|
{};
|
|
|
|
namespace MixedOps
|
|
{
|
|
// This defines all the overloads to make expressions involving
|
|
// std::vector to build expression templates.
|
|
BOOST_PROTO_DEFINE_OPERATORS(IsMixed, MixedDomain)
|
|
|
|
struct assign_op
|
|
{
|
|
template<typename T, typename U>
|
|
void operator ()(T &t, U const &u) const
|
|
{
|
|
t = u;
|
|
}
|
|
};
|
|
|
|
struct plus_assign_op
|
|
{
|
|
template<typename T, typename U>
|
|
void operator ()(T &t, U const &u) const
|
|
{
|
|
t += u;
|
|
}
|
|
};
|
|
|
|
struct minus_assign_op
|
|
{
|
|
template<typename T, typename U>
|
|
void operator ()(T &t, U const &u) const
|
|
{
|
|
t -= u;
|
|
}
|
|
};
|
|
|
|
struct sin_
|
|
{
|
|
template<typename Sig>
|
|
struct result;
|
|
|
|
template<typename This, typename Arg>
|
|
struct result<This(Arg)>
|
|
: boost::remove_const<typename boost::remove_reference<Arg>::type>
|
|
{};
|
|
|
|
template<typename Arg>
|
|
Arg operator ()(Arg const &a) const
|
|
{
|
|
return std::sin(a);
|
|
}
|
|
};
|
|
|
|
template<typename A>
|
|
typename proto::result_of::make_expr<
|
|
proto::tag::function
|
|
, MixedDomain
|
|
, sin_ const
|
|
, A const &
|
|
>::type sin(A const &a)
|
|
{
|
|
return proto::make_expr<proto::tag::function, MixedDomain>(sin_(), boost::ref(a));
|
|
}
|
|
|
|
template<typename FwdIter, typename Expr, typename Op>
|
|
void evaluate(FwdIter begin, FwdIter end, Expr const &expr, Op op)
|
|
{
|
|
IncrementCtx const inc = {};
|
|
DereferenceCtx const deref = {};
|
|
typename boost::result_of<Begin(Expr const &)>::type expr2 = Begin()(expr);
|
|
for(; begin != end; ++begin)
|
|
{
|
|
op(*begin, proto::eval(expr2, deref));
|
|
proto::eval(expr2, inc);
|
|
}
|
|
}
|
|
|
|
// Add-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)
|
|
{
|
|
evaluate(arr.begin(), arr.end(), proto::as_expr<MixedDomain>(expr), assign_op());
|
|
return arr;
|
|
}
|
|
|
|
// Add-assign to a list from some expression.
|
|
template<typename T, typename A, typename Expr>
|
|
std::list<T, A> &assign(std::list<T, A> &arr, Expr const &expr)
|
|
{
|
|
evaluate(arr.begin(), arr.end(), proto::as_expr<MixedDomain>(expr), assign_op());
|
|
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)
|
|
{
|
|
evaluate(arr.begin(), arr.end(), proto::as_expr<MixedDomain>(expr), plus_assign_op());
|
|
return arr;
|
|
}
|
|
|
|
// Add-assign to a list from some expression.
|
|
template<typename T, typename A, typename Expr>
|
|
std::list<T, A> &operator +=(std::list<T, A> &arr, Expr const &expr)
|
|
{
|
|
evaluate(arr.begin(), arr.end(), proto::as_expr<MixedDomain>(expr), plus_assign_op());
|
|
return arr;
|
|
}
|
|
|
|
// Minus-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)
|
|
{
|
|
evaluate(arr.begin(), arr.end(), proto::as_expr<MixedDomain>(expr), minus_assign_op());
|
|
return arr;
|
|
}
|
|
|
|
// Minus-assign to a list from some expression.
|
|
template<typename T, typename A, typename Expr>
|
|
std::list<T, A> &operator -=(std::list<T, A> &arr, Expr const &expr)
|
|
{
|
|
evaluate(arr.begin(), arr.end(), proto::as_expr<MixedDomain>(expr), minus_assign_op());
|
|
return arr;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
using namespace MixedOps;
|
|
|
|
int n = 10;
|
|
std::vector<int> a,b,c,d;
|
|
std::list<double> e;
|
|
std::list<std::complex<double> > f;
|
|
|
|
int i;
|
|
for(i = 0;i < n; ++i)
|
|
{
|
|
a.push_back(i);
|
|
b.push_back(2*i);
|
|
c.push_back(3*i);
|
|
d.push_back(i);
|
|
e.push_back(0.0);
|
|
f.push_back(std::complex<double>(1.0, 1.0));
|
|
}
|
|
|
|
MixedOps::assign(b, 2);
|
|
MixedOps::assign(d, a + b * c);
|
|
a += if_else(d < 30, b, c);
|
|
|
|
MixedOps::assign(e, c);
|
|
e += e - 4 / (c + 1);
|
|
|
|
f -= sin(0.1 * e * std::complex<double>(0.2, 1.2));
|
|
|
|
std::list<double>::const_iterator ei = e.begin();
|
|
std::list<std::complex<double> >::const_iterator fi = f.begin();
|
|
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 << ") = " << *ei++
|
|
<< " f(" << i << ") = " << *fi++
|
|
<< std::endl;
|
|
}
|
|
}
|
|
//]
|