pick.hh 4.9 KB

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