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 //[ vector
7 #include <boost/yap/yap.hpp>
8 
9 #include <vector>
10 #include <iostream>
11 
12 
13 //[ vector_take_nth_xform
14 struct take_nth
15 {
16     template <typename T>
operator ()take_nth17     auto operator() (boost::yap::expr_tag<boost::yap::expr_kind::terminal>,
18                      std::vector<T> const & vec)
19     { return boost::yap::make_terminal(vec[n]); }
20 
21     std::size_t n;
22 };
23 //]
24 
25 // A stateful transform that records whether all the std::vector<> terminals
26 // it has seen are equal to the given size.
27 struct equal_sizes_impl
28 {
29     template <typename T>
operator ()equal_sizes_impl30     auto operator() (boost::yap::expr_tag<boost::yap::expr_kind::terminal>,
31                      std::vector<T> const & vec)
32     {
33         auto const expr_size = vec.size();
34         if (expr_size != size)
35             value = false;
36         return 0;
37     }
38 
39     std::size_t const size;
40     bool value;
41 };
42 
43 template <typename Expr>
equal_sizes(std::size_t size,Expr const & expr)44 bool equal_sizes (std::size_t size, Expr const & expr)
45 {
46     equal_sizes_impl impl{size, true};
47     boost::yap::transform(boost::yap::as_expr(expr), impl);
48     return impl.value;
49 }
50 
51 
52 // Assigns some expression e to the given vector by evaluating e elementwise,
53 // to avoid temporaries and allocations.
54 template <typename T, typename Expr>
assign(std::vector<T> & vec,Expr const & e)55 std::vector<T> & assign (std::vector<T> & vec, Expr const & e)
56 {
57     decltype(auto) expr = boost::yap::as_expr(e);
58     assert(equal_sizes(vec.size(), expr));
59     for (std::size_t i = 0, size = vec.size(); i < size; ++i) {
60         vec[i] = boost::yap::evaluate(
61             boost::yap::transform(boost::yap::as_expr(expr), take_nth{i}));
62     }
63     return vec;
64 }
65 
66 // As assign() above, just using +=.
67 template <typename T, typename Expr>
operator +=(std::vector<T> & vec,Expr const & e)68 std::vector<T> & operator+= (std::vector<T> & vec, Expr const & e)
69 {
70     decltype(auto) expr = boost::yap::as_expr(e);
71     assert(equal_sizes(vec.size(), expr));
72     for (std::size_t i = 0, size = vec.size(); i < size; ++i) {
73         vec[i] += boost::yap::evaluate(
74             boost::yap::transform(boost::yap::as_expr(expr), take_nth{i}));
75     }
76     return vec;
77 }
78 
79 // Define a type trait that identifies std::vectors.
80 template <typename T>
81 struct is_vector : std::false_type {};
82 
83 template <typename T, typename A>
84 struct is_vector<std::vector<T, A>> : std::true_type {};
85 
86 // Define all the expression-returning numeric operators we need.  Each will
87 // accept any std::vector<> as any of its arguments, and then any value in the
88 // remaining argument, if any -- some of the operators below are unary.
89 BOOST_YAP_USER_UDT_UNARY_OPERATOR(negate, boost::yap::expression, is_vector); // -
90 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(multiplies, boost::yap::expression, is_vector); // *
91 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(divides, boost::yap::expression, is_vector); // /
92 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(modulus, boost::yap::expression, is_vector); // %
93 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(plus, boost::yap::expression, is_vector); // +
94 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(minus, boost::yap::expression, is_vector); // -
95 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(less, boost::yap::expression, is_vector); // <
96 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(greater, boost::yap::expression, is_vector); // >
97 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(less_equal, boost::yap::expression, is_vector); // <=
98 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(greater_equal, boost::yap::expression, is_vector); // >=
99 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(equal_to, boost::yap::expression, is_vector); // ==
100 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(not_equal_to, boost::yap::expression, is_vector); // !=
101 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(logical_or, boost::yap::expression, is_vector); // ||
102 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(logical_and, boost::yap::expression, is_vector); // &&
103 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(bitwise_and, boost::yap::expression, is_vector); // &
104 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(bitwise_or, boost::yap::expression, is_vector); // |
105 BOOST_YAP_USER_UDT_ANY_BINARY_OPERATOR(bitwise_xor, boost::yap::expression, is_vector); // ^
106 
main()107 int main()
108 {
109     int i;
110     int const n = 10;
111     std::vector<int> a,b,c,d;
112     std::vector<double> e(n);
113 
114     for (i = 0; i < n; ++i)
115     {
116         a.push_back(i);
117         b.push_back(2*i);
118         c.push_back(3*i);
119         d.push_back(i);
120     }
121 
122     // After this point, no allocations occur.
123 
124     assign(b, 2);
125     assign(d, a + b * c);
126 
127     a += if_else(d < 30, b, c);
128 
129     assign(e, c);
130     e += e - 4 / (c + 1);
131 
132     for (i = 0; i < n; ++i)
133     {
134         std::cout
135             << " a(" << i << ") = " << a[i]
136             << " b(" << i << ") = " << b[i]
137             << " c(" << i << ") = " << c[i]
138             << " d(" << i << ") = " << d[i]
139             << " e(" << i << ") = " << e[i]
140             << std::endl;
141     }
142 
143     return 0;
144 }
145 //]
146