pick.H 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // -*- mode: c++; coding: utf-8 -*-
  2. /// @file pick.H
  3. /// @brief Expression template that picks one of several arguments.
  4. // (c) Daniel Llorens - 2016-2017, 2019
  5. // This library is free software; you can redistribute it and/or modify it under
  6. // the terms of the GNU General Public License as published by the Free
  7. // Software Foundation; either version 3 of the License, or (at your option) any
  8. // later version.
  9. // This class is needed because Expr evaluates its arguments before calling its
  10. // operator. Note that pick() is a normal function, so its *arguments* will always
  11. // be evaluated; it is the individual array elements that will not be.
  12. #pragma once
  13. #include "ra/ply.H"
  14. #include "ra/match.H"
  15. #if defined(RA_CHECK_BOUNDS) && RA_CHECK_BOUNDS==0
  16. #define CHECK_BOUNDS( cond )
  17. #else
  18. #define CHECK_BOUNDS( cond ) RA_ASSERT( cond, 0 )
  19. #endif
  20. namespace ra {
  21. // -----------------
  22. // return type of pick expression, otherwise compiler complains of ambiguity.
  23. // TODO & is crude, maybe is_assignable?
  24. // -----------------
  25. template <class T, class Enable=void>
  26. struct pick_type
  27. {
  28. using type = mp::apply<std::common_type_t, T>;
  29. };
  30. // lvalue
  31. template <class P0, class ... P>
  32. struct pick_type<std::tuple<P0 &, P ...>,
  33. std::enable_if_t<!std::is_const_v<P0> && (std::is_same_v<P0 &, P> && ...)>>
  34. {
  35. using type = P0 &;
  36. };
  37. // const lvalue
  38. template <class P0, class ... P>
  39. struct pick_type<std::tuple<P0 &, P & ...>,
  40. std::enable_if_t<(std::is_same_v<std::decay_t<P0>, std::decay_t<P>> && ...)
  41. && (std::is_const_v<P0> || (std::is_const_v<P> || ...))>>
  42. {
  43. using type = P0 const &;
  44. };
  45. // -----------------
  46. // runtime to compile time conversion for Pick::at() and PickFlat::operator*()
  47. // -----------------
  48. template <class T, class J> struct pick_at_type;
  49. template <class ... P, class J> struct pick_at_type<std::tuple<P ...>, J>
  50. {
  51. using type = typename pick_type<std::tuple<decltype(std::declval<P>().at(std::declval<J>())) ...>>::type;
  52. };
  53. template <class T, class J> using pick_at_t = typename pick_at_type<mp::drop1<std::decay_t<T>>, J>::type;
  54. template <size_t I, class T, class J> inline constexpr
  55. pick_at_t<T, J>
  56. pick_at(size_t p0, T && t, J const & j)
  57. {
  58. if constexpr (I+2<std::tuple_size_v<std::decay_t<T>>) {
  59. if (p0==I) {
  60. return std::get<I+1>(t).at(j);
  61. } else {
  62. return pick_at<I+1>(p0, t, j);
  63. }
  64. } else {
  65. CHECK_BOUNDS(p0==I);
  66. return std::get<I+1>(t).at(j);
  67. }
  68. }
  69. template <class T> struct pick_star_type;
  70. template <class ... P> struct pick_star_type<std::tuple<P ...>>
  71. {
  72. using type = typename pick_type<std::tuple<decltype(*std::declval<P>()) ...>>::type;
  73. };
  74. template <class T> using pick_star_t = typename pick_star_type<mp::drop1<std::decay_t<T>>>::type;
  75. template <size_t I, class T> inline constexpr
  76. pick_star_t<T>
  77. pick_star(size_t p0, T && t)
  78. {
  79. if constexpr (I+2<std::tuple_size_v<std::decay_t<T>>) {
  80. if (p0==I) {
  81. return *(std::get<I+1>(t));
  82. } else {
  83. return pick_star<I+1>(p0, t);
  84. }
  85. } else {
  86. CHECK_BOUNDS(p0==I);
  87. return *(std::get<I+1>(t));
  88. }
  89. }
  90. // -----------------
  91. // follows closely Flat, Expr
  92. // -----------------
  93. // Manipulate ET through flat (raw pointer-like) iterators P ...
  94. template <class T, class I=mp::iota<mp::len<T>>>
  95. struct PickFlat;
  96. template <class P0, class ... P, int ... I>
  97. struct PickFlat<std::tuple<P0, P ...>, mp::int_list<I ...>>
  98. {
  99. std::tuple<P0, P ...> t;
  100. template <class S> void operator+=(S const & s) { ((std::get<I>(t) += std::get<I>(s)), ...); }
  101. decltype(auto) operator*() { return pick_star<0>(*std::get<0>(t), t); }
  102. };
  103. template <class P0, class ... P> inline constexpr auto
  104. pick_flat(P0 && p0, P && ... p)
  105. {
  106. return PickFlat<std::tuple<P0, P ...>> { std::tuple<P0, P ...> { std::forward<P0>(p0), std::forward<P>(p) ... } };
  107. }
  108. template <class P0, class ... P> inline constexpr auto
  109. pick_in(P0 && p0, P && ... p)
  110. {
  111. return Pick<std::tuple<P0, P ...>> { std::forward<P0>(p0), std::forward<P>(p) ... };
  112. }
  113. template <class P0, class ... P> inline constexpr auto
  114. pick(P0 && p0, P && ... p)
  115. {
  116. return pick_in(start(std::forward<P0>(p0)), start(std::forward<P>(p)) ...);
  117. }
  118. // forward decl in atom.H
  119. template <class P0, class ... P, int ... I>
  120. struct Pick<std::tuple<P0, P ...>, mp::int_list<I ...>>: public Match<std::tuple<P0, P ...>>
  121. {
  122. using Match_ = Match<std::tuple<P0, P ...>>;
  123. // see test/ra-9.C [ra01] for forward() here.
  124. constexpr Pick(P0 p0_, P ... p_): Match_(std::forward<P0>(p0_), std::forward<P>(p_) ...) {}
  125. template <class J> constexpr decltype(auto)
  126. at(J const & j)
  127. {
  128. return pick_at<0>(std::get<0>(this->t).at(j), this->t, j);
  129. }
  130. constexpr auto
  131. flat()
  132. {
  133. return pick_flat(std::get<I>(this->t).flat() ...);
  134. }
  135. // needed for xpr with rank_s()==RANK_ANY, which don't decay to scalar when used as operator arguments.
  136. using scalar = decltype(*(pick_flat(std::get<I>(Match_::t).flat() ...)));
  137. operator scalar()
  138. {
  139. if constexpr (this->rank_s()!=1 || size_s(*this)!=1) { // for coord types; so fixed only
  140. if constexpr (this->rank_s()!=0) {
  141. static_assert(this->rank_s()==RANK_ANY);
  142. assert(this->rank()==0);
  143. }
  144. }
  145. return *flat();
  146. }
  147. // forward to make sure value y is not misused as ref [ra05]
  148. #define DEF_ASSIGNOPS(OP) template <class X> constexpr void operator OP(X && x) \
  149. { for_each([](auto && y, auto && x) { std::forward<decltype(y)>(y) OP x; }, *this, x); }
  150. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=)
  151. #undef DEF_ASSIGNOPS
  152. };
  153. } // namespace ra
  154. #undef CHECK_BOUNDS