• 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