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