• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016-2018 T. Zachary Laine
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //[ lazy_vector
7 #include <boost/yap/expression.hpp>
8 
9 #include <algorithm>
10 #include <cassert>
11 #include <iostream>
12 #include <vector>
13 
14 
15 template <boost::yap::expr_kind Kind, typename Tuple>
16 struct lazy_vector_expr;
17 
18 
19 // This transform turns a terminal of std::vector<double> into a terminal
20 // containing the nth double in that vector.  Think of it as turning our
21 // expression of vectors into an expression of scalars.
22 struct take_nth
23 {
24     boost::yap::terminal<lazy_vector_expr, double>
25     operator() (boost::yap::terminal<lazy_vector_expr, std::vector<double>> const & expr);
26 
27     std::size_t n;
28 };
29 
30 // A custom expression template that defines lazy + and - operators that
31 // produce expressions, and an eager [] operator that returns the nth element
32 // of the expression.
33 //[ lazy_vector_decl
34 template <boost::yap::expr_kind Kind, typename Tuple>
35 struct lazy_vector_expr
36 {
37     static const boost::yap::expr_kind kind = Kind;
38 
39     Tuple elements;
40 
41     // Note that this does not return an expression; it is greedily evaluated.
42     auto operator[] (std::size_t n) const;
43 };
44 
BOOST_YAP_USER_BINARY_OPERATOR(plus,lazy_vector_expr,lazy_vector_expr)45 BOOST_YAP_USER_BINARY_OPERATOR(plus, lazy_vector_expr, lazy_vector_expr)
46 BOOST_YAP_USER_BINARY_OPERATOR(minus, lazy_vector_expr, lazy_vector_expr)
47 //]
48 
49 template <boost::yap::expr_kind Kind, typename Tuple>
50 auto lazy_vector_expr<Kind, Tuple>::operator[] (std::size_t n) const
51 { return boost::yap::evaluate(boost::yap::transform(*this, take_nth{n})); }
52 
53 boost::yap::terminal<lazy_vector_expr, double>
operator ()(boost::yap::terminal<lazy_vector_expr,std::vector<double>> const & expr)54 take_nth::operator() (boost::yap::terminal<lazy_vector_expr, std::vector<double>> const & expr)
55 {
56     double x = boost::yap::value(expr)[n];
57     // This move is something of a hack; we're forcing Yap to take a copy of x
58     // by using std::move().  The move indicates that the terminal should keep
59     // the value of x (since, being an rvalue, it may be a temporary), rather
60     // than a reference to x.  See the "How Expression Operands Are Treated"
61     // section of the tutorial for details.
62     return boost::yap::make_terminal<lazy_vector_expr, double>(std::move(x));
63 }
64 
65 // In order to define the += operator with the semantics we want, it's
66 // convenient to derive a terminal type from a terminal instantiation of
67 // lazy_vector_expr.  Note that we could have written a template
68 // specialization here instead -- either one would work.  That would of course
69 // have required more typing.
70 struct lazy_vector :
71     lazy_vector_expr<
72         boost::yap::expr_kind::terminal,
73         boost::hana::tuple<std::vector<double>>
74     >
75 {
lazy_vectorlazy_vector76     lazy_vector () {}
77 
lazy_vectorlazy_vector78     explicit lazy_vector (std::vector<double> && vec)
79     { elements = boost::hana::tuple<std::vector<double>>(std::move(vec)); }
80 
81     template <boost::yap::expr_kind Kind, typename Tuple>
operator +=lazy_vector82     lazy_vector & operator+= (lazy_vector_expr<Kind, Tuple> const & rhs)
83     {
84         std::vector<double> & this_vec = boost::yap::value(*this);
85         for (int i = 0, size = (int)this_vec.size(); i < size; ++i) {
86             this_vec[i] += rhs[i];
87         }
88         return *this;
89     }
90 };
91 
main()92 int main ()
93 {
94     lazy_vector v1{std::vector<double>(4, 1.0)};
95     lazy_vector v2{std::vector<double>(4, 2.0)};
96     lazy_vector v3{std::vector<double>(4, 3.0)};
97 
98     double d1 = (v2 + v3)[2];
99     std::cout << d1 << "\n";
100 
101     v1 += v2 - v3;
102     std::cout << '{' << v1[0] << ',' << v1[1]
103               << ',' << v1[2] << ',' << v1[3] << '}' << "\n";
104 
105     // This expression is disallowed because it does not conform to the
106     // implicit grammar.  operator+= is only defined on terminals, not
107     // arbitrary expressions.
108     // (v2 + v3) += v1;
109 
110     return 0;
111 }
112 //]
113