tarray.cpp 5.4 KB

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