1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
// Copyright (C) 2016-2018 T. Zachary Laine
//
// 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)
//[ tarray
#include <boost/yap/algorithm.hpp>
#include <boost/yap/print.hpp>
#include <array>
#include <iostream>
template <boost::yap::expr_kind Kind, typename Tuple>
struct tarray_expr;
struct take_nth
{
boost::yap::terminal<tarray_expr, int>
operator() (boost::yap::terminal<tarray_expr, std::array<int, 3>> const & expr);
std::size_t n;
};
// Another custom expression template. In this case, we static_assert() that
// it only gets instantiated with terminals with pre-approved value types.
template <boost::yap::expr_kind Kind, typename Tuple>
struct tarray_expr
{
// Make sure that, if this expression is a terminal, its value is one we
// want to support. Note that the presence of expr_kind::expr_ref makes
// life slightly more difficult; we have to account for int const & and
// int & as well as int.
static_assert(
Kind != boost::yap::expr_kind::terminal ||
std::is_same<Tuple, boost::hana::tuple<int const &>>{} ||
std::is_same<Tuple, boost::hana::tuple<int &>>{} ||
std::is_same<Tuple, boost::hana::tuple<int>>{} ||
std::is_same<Tuple, boost::hana::tuple<std::array<int, 3>>>{},
"tarray_expr instantiated with an unsupported terminal type."
);
static const boost::yap::expr_kind kind = Kind;
Tuple elements;
int operator[] (std::size_t n) const
{ return boost::yap::evaluate(boost::yap::transform(*this, take_nth{n})); }
};
// Define operators +, -, *, and /.
BOOST_YAP_USER_BINARY_OPERATOR(plus, tarray_expr, tarray_expr)
BOOST_YAP_USER_BINARY_OPERATOR(minus, tarray_expr, tarray_expr)
BOOST_YAP_USER_BINARY_OPERATOR(multiplies, tarray_expr, tarray_expr)
BOOST_YAP_USER_BINARY_OPERATOR(divides, tarray_expr, tarray_expr)
boost::yap::terminal<tarray_expr, int>
take_nth::operator() (boost::yap::terminal<tarray_expr, std::array<int, 3>> const & expr)
{
int x = boost::yap::value(expr)[n];
// Again, this is the move hack to get x into the resulting terminal as a
// value instead of a reference.
return boost::yap::make_terminal<tarray_expr>(std::move(x));
}
// Stream-out operators for the two kinds of terminals we support.
std::ostream & operator<< (std::ostream & os, boost::yap::terminal<tarray_expr, int> expr)
{ return os << '{' << boost::yap::value(expr) << '}'; }
std::ostream & operator<< (std::ostream & os, boost::yap::terminal<tarray_expr, std::array<int, 3>> expr)
{
std::array<int, 3> const & a = boost::yap::value(expr);
return os << '{' << a[0] << ", " << a[1] << ", " << a[2] << '}';
}
// Stream-out operators for general expressions. Note that we have to treat
// the reference case separately; this also could have been done using
// constexpr if in a single function template.
template <typename Tuple>
std::ostream & operator<< (std::ostream & os, tarray_expr<boost::yap::expr_kind::expr_ref, Tuple> const & expr)
{ return os << boost::yap::deref(expr); }
template <boost::yap::expr_kind Kind, typename Tuple>
std::ostream & operator<< (std::ostream & os, tarray_expr<Kind, Tuple> const & expr)
{
if (Kind == boost::yap::expr_kind::plus || Kind == boost::yap::expr_kind::minus)
os << '(';
os << boost::yap::left(expr) << " " << op_string(Kind) << " " << boost::yap::right(expr);
if (Kind == boost::yap::expr_kind::plus || Kind == boost::yap::expr_kind::minus)
os << ')';
return os;
}
// Since we want different behavior on terminals than on other kinds of
// expressions, we create a custom type that does so.
struct tarray :
tarray_expr<
boost::yap::expr_kind::terminal,
boost::hana::tuple<std::array<int, 3>>
>
{
explicit tarray (int i = 0, int j = 0, int k = 0)
{
(*this)[0] = i;
(*this)[1] = j;
(*this)[2] = k;
}
explicit tarray (std::array<int, 3> a)
{
(*this)[0] = a[0];
(*this)[1] = a[1];
(*this)[2] = a[2];
}
int & operator[] (std::ptrdiff_t i)
{ return boost::yap::value(*this)[i]; }
int const & operator[] (std::ptrdiff_t i) const
{ return boost::yap::value(*this)[i]; }
template <typename T>
tarray & operator= (T const & t)
{
// We use as_expr() here to make sure that the value passed to
// assign() is an expression. as_expr() simply forwards expressions
// through, and wraps non-expressions as terminals.
return assign(boost::yap::as_expr< ::tarray_expr>(t));
}
template <typename Expr>
tarray & printAssign (Expr const & expr)
{
*this = expr;
std::cout << *this << " = " << expr << std::endl;
return *this;
}
private:
template <typename Expr>
tarray & assign (Expr const & expr)
{
(*this)[0] = expr[0];
(*this)[1] = expr[1];
(*this)[2] = expr[2];
return *this;
}
};
int main()
{
tarray a(3,1,2);
tarray b;
std::cout << a << std::endl;
std::cout << b << std::endl;
b[0] = 7; b[1] = 33; b[2] = -99;
tarray c(a);
std::cout << c << std::endl;
a = 0;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
a = b + c;
std::cout << a << std::endl;
a.printAssign(b+c*(b + 3*c));
return 0;
}
//]
|