Low-Level Abstraction of Memory Access
RecordRef.hpp
Go to the documentation of this file.
1 // Copyright 2022 Alexander Matthes, Bernhard Manfred Gruber
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #pragma once
5 
6 #include "Concepts.hpp"
7 #include "ProxyRefOpMixin.hpp"
8 #include "StructName.hpp"
9 #include "View.hpp"
10 #include "macros.hpp"
11 
12 #include <boost/functional/hash.hpp>
13 #include <iosfwd>
14 #include <type_traits>
15 
16 namespace llama
17 {
19  template<typename View, typename BoundRecordCoord, bool OwnView>
20  struct RecordRef;
21 
23  template<typename View>
24  inline constexpr auto isRecordRef = false;
25 
27  template<typename View, typename BoundRecordCoord, bool OwnView>
28  inline constexpr auto isRecordRef<RecordRef<View, BoundRecordCoord, OwnView>> = true;
29 
32  template<typename View, typename BoundRecordCoord, bool OwnView>
34  {
36  One<RecordDim> temp;
37  temp = rr;
38  return temp;
39  }
40 
41  namespace internal
42  {
43  template<
44  typename Functor,
45  typename LeftRecord,
46  typename RightView,
47  typename RightBoundRecordDim,
48  bool RightOwnView>
50  LeftRecord& left,
52  {
54  // if the record dimension left and right is the same, a single loop is enough and no tag check is needed.
55  // this safes a lot of compilation time.
56  using LARD = typename LeftRecord::AccessibleRecordDim;
57  using RARD = typename RightRecord::AccessibleRecordDim;
58  if constexpr(std::is_same_v<LARD, RARD>)
59  {
60  forEachLeafCoord<LARD>([&](auto rc) LLAMA_LAMBDA_INLINE { Functor{}(left(rc), right(rc)); });
61  }
62  else
63  {
64  forEachLeafCoord<LARD>(
65  [&](auto leftRC) LLAMA_LAMBDA_INLINE
66  {
67  using LeftInnerCoord = decltype(leftRC);
68  forEachLeafCoord<RARD>(
69  [&](auto rightRC) LLAMA_LAMBDA_INLINE
70  {
71  using RightInnerCoord = decltype(rightRC);
72  if constexpr(hasSameTags<LARD, LeftInnerCoord, RARD, RightInnerCoord>)
73  {
74  Functor{}(left(leftRC), right(rightRC));
75  }
76  });
77  });
78  }
79  return left;
80  }
81 
82  template<typename Functor, typename LeftRecord, typename T>
83  LLAMA_FN_HOST_ACC_INLINE auto recordRefArithOperator(LeftRecord& left, const T& right) -> LeftRecord&
84  {
85  forEachLeafCoord<typename LeftRecord::AccessibleRecordDim>([&](auto leftRC) LLAMA_LAMBDA_INLINE
86  { Functor{}(left(leftRC), right); });
87  return left;
88  }
89 
90  template<
91  typename Functor,
92  typename LeftRecord,
93  typename RightView,
94  typename RightBoundRecordDim,
95  bool RightOwnView>
97  const LeftRecord& left,
99  {
101  bool result = true;
102  // if the record dimension left and right is the same, a single loop is enough and no tag check is needed.
103  // this safes a lot of compilation time.
104  using LARD = typename LeftRecord::AccessibleRecordDim;
105  using RARD = typename RightRecord::AccessibleRecordDim;
106  if constexpr(std::is_same_v<LARD, RARD>)
107  {
108  forEachLeafCoord<LARD>([&](auto rc) LLAMA_LAMBDA_INLINE { result &= Functor{}(left(rc), right(rc)); });
109  }
110  else
111  {
112  forEachLeafCoord<LARD>(
113  [&](auto leftRC) LLAMA_LAMBDA_INLINE
114  {
115  using LeftInnerCoord = decltype(leftRC);
116  forEachLeafCoord<RARD>(
117  [&](auto rightRC) LLAMA_LAMBDA_INLINE
118  {
119  using RightInnerCoord = decltype(rightRC);
120  if constexpr(hasSameTags<LARD, LeftInnerCoord, RARD, RightInnerCoord>)
121  {
122  result &= Functor{}(left(leftRC), right(rightRC));
123  }
124  });
125  });
126  }
127  return result;
128  }
129 
130  template<typename Functor, typename LeftRecord, typename T>
131  LLAMA_FN_HOST_ACC_INLINE auto recordRefRelOperator(const LeftRecord& left, const T& right) -> bool
132  {
133  bool result = true;
134  forEachLeafCoord<typename LeftRecord::AccessibleRecordDim>([&](auto leftRC) LLAMA_LAMBDA_INLINE
135  { result &= Functor{}(left(leftRC), right); });
136  return result;
137  }
138 
139  struct Assign
140  {
141  template<typename A, typename B>
142  LLAMA_FN_HOST_ACC_INLINE auto operator()(A&& a, const B& b) const -> decltype(auto)
143  {
144  return std::forward<A>(a) = b;
145  }
146  };
147 
148  struct PlusAssign
149  {
150  template<typename A, typename B>
151  LLAMA_FN_HOST_ACC_INLINE auto operator()(A&& a, const B& b) const -> decltype(auto)
152  {
153  return std::forward<A>(a) += b;
154  }
155  };
156 
157  struct MinusAssign
158  {
159  template<typename A, typename B>
160  LLAMA_FN_HOST_ACC_INLINE auto operator()(A&& a, const B& b) const -> decltype(auto)
161  {
162  return std::forward<A>(a) -= b;
163  }
164  };
165 
167  {
168  template<typename A, typename B>
169  LLAMA_FN_HOST_ACC_INLINE auto operator()(A&& a, const B& b) const -> decltype(auto)
170  {
171  return std::forward<A>(a) *= b;
172  }
173  };
174 
176  {
177  template<typename A, typename B>
178  LLAMA_FN_HOST_ACC_INLINE auto operator()(A&& a, const B& b) const -> decltype(auto)
179  {
180  return std::forward<A>(a) /= b;
181  }
182  };
183 
185  {
186  template<typename A, typename B>
187  LLAMA_FN_HOST_ACC_INLINE auto operator()(A&& a, const B& b) const -> decltype(auto)
188  {
189  return std::forward<A>(a) %= b;
190  }
191  };
192 
193  template<
194  typename ProxyReference,
195  typename T,
196  std::enable_if_t<!isRecordRef<std::decay_t<ProxyReference>>, int> = 0>
197  LLAMA_FN_HOST_ACC_INLINE auto asTupleImpl(ProxyReference&& leaf, T) -> ProxyReference
198  {
199  return leaf;
200  }
201 
202  template<
203  typename TWithOptionalConst,
204  typename T,
205  std::enable_if_t<!isRecordRef<std::decay_t<TWithOptionalConst>>, int> = 0>
206  LLAMA_FN_HOST_ACC_INLINE auto asTupleImpl(TWithOptionalConst& leaf, T)
207  -> std::reference_wrapper<TWithOptionalConst>
208  {
209  return leaf;
210  }
211 
212  template<typename RecordRef, typename T, std::size_t N, std::size_t... Is>
213  LLAMA_FN_HOST_ACC_INLINE auto asTupleImplForArray(RecordRef&& vd, T (&&)[N], std::index_sequence<Is...>)
214  {
215  return std::make_tuple(asTupleImpl(vd(RecordCoord<Is>{}), T{})...);
216  }
217 
218  template<typename RecordRef, typename T, std::size_t N>
220  {
221  return asTupleImplForArray(std::forward<RecordRef>(vd), std::move(a), std::make_index_sequence<N>{});
222  }
223 
224  template<typename RecordRef, typename... Fields>
226  {
227  return std::make_tuple(asTupleImpl(vd(GetFieldTag<Fields>{}), GetFieldType<Fields>{})...);
228  }
229 
230  template<
231  typename ProxyReference,
232  typename T,
233  std::enable_if_t<!isRecordRef<std::decay_t<ProxyReference>>, int> = 0>
234  LLAMA_FN_HOST_ACC_INLINE auto asFlatTupleImpl(ProxyReference&& leaf, T) -> std::tuple<ProxyReference>
235  {
236  static_assert(!std::is_reference_v<ProxyReference>);
237  return {std::move(leaf)}; // NOLINT(bugprone-move-forwarding-reference)
238  }
239 
240  template<
241  typename TWithOptionalConst,
242  typename T,
243  std::enable_if_t<!isRecordRef<std::decay_t<TWithOptionalConst>>, int> = 0>
244  LLAMA_FN_HOST_ACC_INLINE auto asFlatTupleImpl(TWithOptionalConst& leaf, T) -> std::tuple<TWithOptionalConst&>
245  {
246  return {leaf};
247  }
248 
249  template<typename RecordRef, typename T, std::size_t N, std::size_t... Is>
250  LLAMA_FN_HOST_ACC_INLINE auto asFlatTupleImplForArray(RecordRef&& vd, T (&&)[N], std::index_sequence<Is...>)
251  {
252  return std::tuple_cat(asFlatTupleImpl(vd(RecordCoord<Is>{}), T{})...);
253  }
254 
255  template<typename RecordRef, typename T, std::size_t N>
257  {
258  return asFlatTupleImplForArray(std::forward<RecordRef>(vd), std::move(a), std::make_index_sequence<N>{});
259  }
260 
261  template<typename RecordRef, typename... Fields>
263  {
264  return std::tuple_cat(asFlatTupleImpl(vd(GetFieldTag<Fields>{}), GetFieldType<Fields>{})...);
265  }
266 
267  template<typename T, typename = void>
268  inline constexpr auto isTupleLike = false;
269 
270  // get<I>(t) and std::tuple_size<T> must be available
271  using std::get; // make sure a get<0>() can be found, so the compiler can compile the trait
272  template<typename T>
273  inline constexpr auto isTupleLike<T, std::void_t<decltype(get<0>(std::declval<T>())), std::tuple_size<T>>>
274  = true;
275 
276  template<typename... Ts>
277  inline constexpr auto dependentFalse = false;
278 
279  template<typename Tuple1, typename Tuple2, std::size_t... Is>
280  LLAMA_FN_HOST_ACC_INLINE void assignTuples(Tuple1&& dst, Tuple2&& src, std::index_sequence<Is...>);
281 
282  template<typename T1, typename T2>
284  {
285  if constexpr(isTupleLike<std::decay_t<T1>> && isTupleLike<std::decay_t<T2>>)
286  {
287  static_assert(std::tuple_size_v<std::decay_t<T1>> == std::tuple_size_v<std::decay_t<T2>>);
288  assignTuples(dst, src, std::make_index_sequence<std::tuple_size_v<std::decay_t<T1>>>{});
289  }
290  else if constexpr(!isTupleLike<std::decay_t<T1>> && !isTupleLike<std::decay_t<T2>>)
291  std::forward<T1>(dst) = std::forward<T2>(src);
292  else
293  static_assert(
294  dependentFalse<T1, T2>,
295  "Elements to assign are not tuple/tuple or non-tuple/non-tuple.");
296  }
297 
298  template<typename Tuple1, typename Tuple2, std::size_t... Is>
299  LLAMA_FN_HOST_ACC_INLINE void assignTuples(Tuple1&& dst, Tuple2&& src, std::index_sequence<Is...>)
300  {
301  static_assert(std::tuple_size_v<std::decay_t<Tuple1>> == std::tuple_size_v<std::decay_t<Tuple2>>);
302  using std::get;
303  (assignTupleElement(get<Is>(std::forward<Tuple1>(dst)), get<Is>(std::forward<Tuple2>(src))), ...);
304  }
305 
306  template<typename T, typename Tuple, std::size_t... Is>
307  LLAMA_FN_HOST_ACC_INLINE auto makeFromTuple(Tuple&& src, std::index_sequence<Is...>)
308  {
309  using std::get;
310  return T{get<Is>(src)...}; // no forward of src, since we call get multiple times on it
311  }
312 
313  template<typename T, typename SFINAE, typename... Args>
314  inline constexpr auto isDirectListInitializableImpl = false;
315 
316  template<typename T, typename... Args>
317  inline constexpr auto
318  isDirectListInitializableImpl<T, std::void_t<decltype(T{std::declval<Args>()...})>, Args...>
319  = true;
320 
321  template<typename T, typename... Args>
322  inline constexpr auto isDirectListInitializable = isDirectListInitializableImpl<T, void, Args...>;
323 
324  template<typename T, typename Tuple>
325  inline constexpr auto isDirectListInitializableFromTuple = false;
326 
327  template<typename T, template<typename...> typename Tuple, typename... Args>
329  = isDirectListInitializable<T, Args...>;
330 
331  template<typename T, typename Simd, typename SrcRC, typename DstRC>
332  LLAMA_FN_HOST_ACC_INLINE void loadSimdFromField(const T& srcRef, Simd& dstSimd, SrcRC srcRC, DstRC dstRC);
333 
334  template<typename Simd, typename TFwd, typename SrcRC, typename DstRC>
335  LLAMA_FN_HOST_ACC_INLINE void storeSimdToField(const Simd& srcSimd, TFwd&& dstRef, SrcRC srcRC, DstRC dstRC);
336  } // namespace internal
337 
344  template<typename TView, typename TBoundRecordCoord, bool OwnView>
345  struct RecordRef : private TView::Mapping::ArrayExtents::Index
346  {
347  using View = TView;
349  = TBoundRecordCoord;
350 
351  private:
352  using ArrayIndex = typename View::Mapping::ArrayExtents::Index;
353  using RecordDim = typename View::Mapping::RecordDim;
354 
355  std::conditional_t<OwnView, View, View&> view;
356 
357  public:
361 
364  /* requires(OwnView) */
365  : ArrayIndex{}
366  , view{allocScalarView<0, RecordDim>()}
367  {
368  static_assert(OwnView, "The default constructor of RecordRef is only available if it owns the view.");
369  }
370 
372  RecordRef(ArrayIndex ai, std::conditional_t<OwnView, View&&, View&> view)
373  : ArrayIndex{ai}
374  , view{static_cast<decltype(view)>(view)}
375  {
376  }
377 
378  RecordRef(const RecordRef&) = default;
379 
380  // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
382  {
383  // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator)
384  return this->operator=<RecordRef>(other);
385  }
386 
387  RecordRef(RecordRef&&) noexcept = default;
388  auto operator=(RecordRef&&) noexcept -> RecordRef& = default;
389 
390  ~RecordRef() = default;
391 
392  LLAMA_FN_HOST_ACC_INLINE constexpr auto arrayIndex() const -> ArrayIndex
393  {
394  return static_cast<const ArrayIndex&>(*this);
395  }
396 
399  template<typename OtherView, typename OtherBoundRecordCoord, bool OtherOwnView>
400  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
402  /* requires(OwnView) */
403  : RecordRef()
404  {
405  static_assert(
406  OwnView,
407  "The copy constructor of RecordRef from a different RecordRef is only available if it owns "
408  "the "
409  "view.");
410  *this = recordRef;
411  }
412 
413  // TODO(bgruber): unify with previous in C++20 and use explicit(cond)
415  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
416  LLAMA_FN_HOST_ACC_INLINE explicit RecordRef(const T& scalar)
417  /* requires(OwnView) */
418  : RecordRef()
419  {
420  static_assert(
421  OwnView,
422  "The constructor of RecordRef from a scalar is only available if it owns the view.");
423  *this = scalar;
424  }
425 
429  template<std::size_t... Coord>
431  {
432  using AbsolutCoord = Cat<BoundRecordCoord, RecordCoord<Coord...>>;
433  using AccessedType = GetType<RecordDim, AbsolutCoord>;
434  if constexpr(isRecordDim<AccessedType>)
435  return RecordRef<const View, AbsolutCoord>{arrayIndex(), this->view};
436  else
437  return this->view.access(arrayIndex(), AbsolutCoord{});
438  }
439 
440  // FIXME(bgruber): remove redundancy
441  template<std::size_t... Coord>
443  {
444  using AbsolutCoord = Cat<BoundRecordCoord, RecordCoord<Coord...>>;
445  using AccessedType = GetType<RecordDim, AbsolutCoord>;
446  if constexpr(isRecordDim<AccessedType>)
447  return RecordRef<View, AbsolutCoord>{arrayIndex(), this->view};
448  else
449  return this->view.access(arrayIndex(), AbsolutCoord{});
450  }
451 
455  template<typename... Tags>
456  LLAMA_FN_HOST_ACC_INLINE auto operator()(Tags...) const -> decltype(auto)
457  {
459  return operator()(RecordCoord{});
460  }
461 
462  // FIXME(bgruber): remove redundancy
463  template<typename... Tags>
464  LLAMA_FN_HOST_ACC_INLINE auto operator()(Tags...) -> decltype(auto)
465  {
467  return operator()(RecordCoord{});
468  }
469 
470 #ifdef LLAMA_HAS_STRING_FIELDS
472  template<internal::FixedString Name>
473  LLAMA_FN_HOST_ACC_INLINE auto at() const -> decltype(auto)
474  {
476  return operator()(RecordCoord{});
477  }
478 
479  // FIXME(bgruber): remove redundancy
481  template<internal::FixedString Name>
482  LLAMA_FN_HOST_ACC_INLINE auto at() -> decltype(auto)
483  {
484  using RecordCoord = GetCoordFromTags<AccessibleRecordDim, internal::StringTag<Name>>;
485  return operator()(RecordCoord{});
486  }
487 #endif
488 
489  template<typename T>
491  {
492  // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator)
493  return internal::recordRefArithOperator<internal::Assign>(*this, other);
494  }
495 
496  template<typename T>
498  {
499  return internal::recordRefArithOperator<internal::PlusAssign>(*this, other);
500  }
501 
502  template<typename T>
504  {
505  return internal::recordRefArithOperator<internal::MinusAssign>(*this, other);
506  }
507 
508  template<typename T>
510  {
511  return internal::recordRefArithOperator<internal::MultiplyAssign>(*this, other);
512  }
513 
514  template<typename T>
516  {
517  return internal::recordRefArithOperator<internal::DivideAssign>(*this, other);
518  }
519 
520  template<typename T>
522  {
523  return internal::recordRefArithOperator<internal::ModuloAssign>(*this, other);
524  }
525 
526  template<typename T>
527  LLAMA_FN_HOST_ACC_INLINE friend auto operator+(const RecordRef& vd, const T& t)
528  {
529  return copyRecord(vd) += t;
530  }
531 
532  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
533  LLAMA_FN_HOST_ACC_INLINE friend auto operator+(const T& t, const RecordRef& vd)
534  {
535  return vd + t;
536  }
537 
538  template<typename T>
539  LLAMA_FN_HOST_ACC_INLINE friend auto operator-(const RecordRef& vd, const T& t)
540  {
541  return copyRecord(vd) -= t;
542  }
543 
544  template<typename T>
545  LLAMA_FN_HOST_ACC_INLINE friend auto operator*(const RecordRef& vd, const T& t)
546  {
547  return copyRecord(vd) *= t;
548  }
549 
550  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
551  LLAMA_FN_HOST_ACC_INLINE friend auto operator*(const T& t, const RecordRef& vd)
552  {
553  return vd * t;
554  }
555 
556  template<typename T>
557  LLAMA_FN_HOST_ACC_INLINE friend auto operator/(const RecordRef& vd, const T& t)
558  {
559  return copyRecord(vd) /= t;
560  }
561 
562  template<typename T>
563  LLAMA_FN_HOST_ACC_INLINE friend auto operator%(const RecordRef& vd, const T& t)
564  {
565  return copyRecord(vd) %= t;
566  }
567 
568  template<typename T>
569  LLAMA_FN_HOST_ACC_INLINE friend auto operator==(const RecordRef& vd, const T& t) -> bool
570  {
571  return internal::recordRefRelOperator<std::equal_to<>>(vd, t);
572  }
573 
574  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
575  LLAMA_FN_HOST_ACC_INLINE friend auto operator==(const T& t, const RecordRef& vd) -> bool
576  {
577  return vd == t;
578  }
579 
580  template<typename T>
581  LLAMA_FN_HOST_ACC_INLINE friend auto operator!=(const RecordRef& vd, const T& t) -> bool
582  {
583  return !(vd == t);
584  }
585 
586  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
587  LLAMA_FN_HOST_ACC_INLINE friend auto operator!=(const T& t, const RecordRef& vd) -> bool
588  {
589  return !(t == vd);
590  }
591 
592  template<typename T>
593  LLAMA_FN_HOST_ACC_INLINE friend auto operator<(const RecordRef& vd, const T& t) -> bool
594  {
595  return internal::recordRefRelOperator<std::less<>>(vd, t);
596  }
597 
598  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
599  LLAMA_FN_HOST_ACC_INLINE friend auto operator<(const T& t, const RecordRef& vd) -> bool
600  {
601  return vd > t;
602  }
603 
604  template<typename T>
605  LLAMA_FN_HOST_ACC_INLINE friend auto operator<=(const RecordRef& vd, const T& t) -> bool
606  {
607  return internal::recordRefRelOperator<std::less_equal<>>(vd, t);
608  }
609 
610  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
611  LLAMA_FN_HOST_ACC_INLINE friend auto operator<=(const T& t, const RecordRef& vd) -> bool
612  {
613  return vd >= t;
614  }
615 
616  template<typename T>
617  LLAMA_FN_HOST_ACC_INLINE friend auto operator>(const RecordRef& vd, const T& t) -> bool
618  {
619  return internal::recordRefRelOperator<std::greater<>>(vd, t);
620  }
621 
622  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
623  LLAMA_FN_HOST_ACC_INLINE friend auto operator>(const T& t, const RecordRef& vd) -> bool
624  {
625  return vd < t;
626  }
627 
628  template<typename T>
629  LLAMA_FN_HOST_ACC_INLINE friend auto operator>=(const RecordRef& vd, const T& t) -> bool
630  {
631  return internal::recordRefRelOperator<std::greater_equal<>>(vd, t);
632  }
633 
634  template<typename T, typename = std::enable_if_t<!isRecordRef<T>>>
635  LLAMA_FN_HOST_ACC_INLINE friend auto operator>=(const T& t, const RecordRef& vd) -> bool
636  {
637  return vd <= t;
638  }
639 
641  {
643  }
644 
646  {
648  }
649 
651  {
653  }
654 
656  {
658  }
659 
660  template<std::size_t I>
661  LLAMA_FN_HOST_ACC_INLINE auto get() -> decltype(auto)
662  {
663  return operator()(RecordCoord<I>{});
664  }
665 
666  template<std::size_t I>
667  LLAMA_FN_HOST_ACC_INLINE auto get() const -> decltype(auto)
668  {
669  return operator()(RecordCoord<I>{});
670  }
671 
672  template<typename TupleLike>
673  LLAMA_FN_HOST_ACC_INLINE auto loadAs() -> TupleLike
674  {
675  static_assert(
677  "TupleLike must be constructible from as many values as this RecordRef recursively represents "
678  "like "
679  "this: TupleLike{values...}");
680  return internal::makeFromTuple<TupleLike>(
681  asFlatTuple(),
682  std::make_index_sequence<std::tuple_size_v<decltype(asFlatTuple())>>{});
683  }
684 
685  template<typename TupleLike>
686  LLAMA_FN_HOST_ACC_INLINE auto loadAs() const -> TupleLike
687  {
688  static_assert(
690  "TupleLike must be constructible from as many values as this RecordRef recursively represents "
691  "like "
692  "this: TupleLike{values...}");
693  return internal::makeFromTuple<TupleLike>(
694  asFlatTuple(),
695  std::make_index_sequence<std::tuple_size_v<decltype(asFlatTuple())>>{});
696  }
697 
698  struct Loader
699  {
701 
702  template<typename T>
703  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
705  {
706  return vd.loadAs<T>();
707  }
708  };
709 
710  struct LoaderConst
711  {
712  const RecordRef& vd;
713 
714  template<typename T>
715  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
716  LLAMA_FN_HOST_ACC_INLINE operator T() const
717  {
718  return vd.loadAs<T>();
719  }
720  };
721 
723  {
724  return {*this};
725  }
726 
728  {
729  return {*this};
730  }
731 
732  template<typename TupleLike>
733  LLAMA_FN_HOST_ACC_INLINE void store(const TupleLike& t)
734  {
735  internal::assignTuples(asTuple(), t, std::make_index_sequence<std::tuple_size_v<TupleLike>>{});
736  }
737 
738  // swap for equal RecordRef
740  std::conditional_t<OwnView, RecordRef&, RecordRef> a,
741  std::conditional_t<OwnView, RecordRef&, RecordRef> b) noexcept
742  {
743  forEachLeafCoord<AccessibleRecordDim>(
744  [&](auto rc) LLAMA_LAMBDA_INLINE
745  {
746  using std::swap;
748  // FIXME(bgruber): swap is constexpr in C++20, so nvcc rightfully complains that we call a __host__
749  // function here. But we must call ADL swap, so we can pick up any swap() for any user defined type
750  // in the record dimension. Let's see if this ever hits us. Moving to C++20 will solve it.
751  swap(a(rc), b(rc));
753  });
754  }
755 
756  // FIXME(bgruber): the SIMD load/store functions need to navigate back from a record ref to the contained view
757  // to find subsequent elements. This is not a great design for now and the SIMD load/store functions should
758  // probably take iterators to records.
759  template<typename T, typename Simd, typename SrcRC, typename DstRC>
761  const T& srcRef,
762  Simd& dstSimd,
763  SrcRC srcRC,
764  DstRC dstRC);
765  template<typename Simd, typename TFwd, typename SrcRC, typename DstRC>
767  const Simd& srcSimd,
768  TFwd&& dstRef,
769  SrcRC srcRC,
770  DstRC dstRC);
771  };
772 
773  // swap for heterogeneous RecordRef
775  template<
776  typename ViewA,
777  typename BoundRecordDimA,
778  bool OwnViewA,
779  typename ViewB,
780  typename BoundRecordDimB,
781  bool OwnViewB>
785  -> std::enable_if_t<std::is_same_v<
788  {
790  forEachLeafCoord<typename LeftRecord::AccessibleRecordDim>(
791  [&](auto rc) LLAMA_LAMBDA_INLINE
792  {
793  using std::swap;
794  swap(a(rc), b(rc));
795  });
796  }
797 
799  template<typename View, typename BoundRecordCoord, bool OwnView>
800  auto operator<<(std::ostream& os, const RecordRef<View, BoundRecordCoord, OwnView>& vr) -> std::ostream&
801  {
803  os << "{";
804  if constexpr(std::is_array_v<RecordDim>)
805  {
806  mp_for_each_inline<mp_iota_c<std::extent_v<RecordDim>>>(
807  [&](auto ic)
808  {
809  constexpr std::size_t i = decltype(ic)::value;
810  if(i > 0)
811  os << ", ";
812  os << '[' << i << ']' << ": " << vr(RecordCoord<i>{});
813  });
814  }
815  else
816  {
817  mp_for_each_inline<mp_iota<mp_size<RecordDim>>>(
818  [&](auto ic)
819  {
820  constexpr std::size_t i = decltype(ic)::value;
821  if(i > 0)
822  os << ", ";
823  using Field = mp_at_c<RecordDim, i>;
824  os << structName<GetFieldTag<Field>>() << ": " << vr(RecordCoord<i>{});
825  });
826  }
827  os << "}";
828  return os;
829  }
830 
832  template<typename RecordRefFwd, typename Functor>
833  LLAMA_FN_HOST_ACC_INLINE constexpr void forEachLeaf(RecordRefFwd&& vr, Functor&& functor)
834  {
835  using RecordRef = std::remove_reference_t<RecordRefFwd>;
836  forEachLeafCoord<typename RecordRef::AccessibleRecordDim>(
837  [functor = std::forward<Functor>(functor), &vr = vr](auto rc)
838  LLAMA_LAMBDA_INLINE_WITH_SPECIFIERS(constexpr mutable) { std::forward<Functor>(functor)(vr(rc)); });
839  }
840 
841  namespace internal
842  {
843  // gets the value type for a given T, where T models either a value, an l-value reference, a proxy reference or
844  // a RecordRef.
845  template<typename T, typename = void>
846  struct ValueOf
847  {
848  using type = T;
849  };
850 
851  template<typename T>
852  struct ValueOf<T, std::enable_if_t<isRecordRef<T>>>
853  {
855  };
856 
857 #ifdef __cpp_lib_concepts
858  template<ProxyReference T>
859 #else
860  template<typename T>
861 #endif
862  struct ValueOf<T, std::enable_if_t<isProxyReference<T>>>
863  {
864  using type = typename T::value_type;
865  };
866 
867  template<typename T>
868  struct ValueOf<T&>
869  {
870  using type = std::remove_const_t<T>;
871  };
872  } // namespace internal
873 
876  template<typename T>
878  {
879  return std::forward<T>(valueOrRef);
880  }
881 
887  template<typename Reference, typename = void>
888  struct ScopedUpdate : internal::ValueOf<Reference>::type
889  {
891 
893  LLAMA_FN_HOST_ACC_INLINE explicit ScopedUpdate(Reference r) : value_type(r), ref(r)
894  {
895  }
896 
897  ScopedUpdate(const ScopedUpdate&) = delete;
898  auto operator=(const ScopedUpdate&) -> ScopedUpdate& = delete;
899 
900  ScopedUpdate(ScopedUpdate&&) noexcept = default;
901  auto operator=(ScopedUpdate&&) noexcept -> ScopedUpdate& = default;
902 
903  using value_type::operator=;
904 
907  {
908  ref = static_cast<value_type&>(*this);
909  }
910 
913  {
914  return *this;
915  }
916 
918  LLAMA_FN_HOST_ACC_INLINE auto get() const -> const value_type&
919  {
920  return *this;
921  }
922 
923  private:
924  Reference ref;
925  };
926 
928  template<typename Reference>
929  struct ScopedUpdate<
930  Reference,
931  std::enable_if_t<std::is_fundamental_v<typename internal::ValueOf<Reference>::type>>>
932  : ProxyRefOpMixin<ScopedUpdate<Reference>, typename internal::ValueOf<Reference>::type>
933  {
935 
936  LLAMA_FN_HOST_ACC_INLINE explicit ScopedUpdate(Reference r) : value(r), ref(r)
937  {
938  }
939 
940  ScopedUpdate(const ScopedUpdate&) = delete;
941  auto operator=(const ScopedUpdate&) -> ScopedUpdate& = delete;
942 
943  ScopedUpdate(ScopedUpdate&&) noexcept = default;
944  auto operator=(ScopedUpdate&&) noexcept -> ScopedUpdate& = default;
945 
947  {
948  return value;
949  }
950 
951  LLAMA_FN_HOST_ACC_INLINE auto get() const -> const value_type&
952  {
953  return value;
954  }
955 
956  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
957  LLAMA_FN_HOST_ACC_INLINE operator const value_type&() const
958  {
959  return value;
960  }
961 
962  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
964  {
965  return value;
966  }
967 
969  {
970  value = v;
971  return *this;
972  }
973 
975  {
976  ref = value;
977  }
978 
979  private:
980  value_type value;
981  Reference ref;
982  };
983 
984  namespace internal
985  {
986  template<typename T, typename = void>
987  struct ReferenceTo
988  {
989  using type = T&;
990  };
991 
992  template<typename T>
993  struct ReferenceTo<T, std::enable_if_t<isRecordRef<T> && !isOne<T>>>
994  {
995  using type = T;
996  };
997 
998 #ifdef __cpp_lib_concepts
999  template<ProxyReference T>
1000 #else
1001  template<typename T>
1002 #endif
1003  struct ReferenceTo<T, std::enable_if_t<isProxyReference<T>>>
1004  {
1005  using type = T;
1006  };
1007  } // namespace internal
1008 
1009  LLAMA_EXPORT
1010  template<typename T>
1012 } // namespace llama
1013 
1015 template<typename View, typename BoundRecordCoord, bool OwnView>
1016 struct std::tuple_size<llama::RecordRef<View, BoundRecordCoord, OwnView>> // NOLINT(cert-dcl58-cpp)
1017  : boost::mp11::mp_size<typename llama::RecordRef<View, BoundRecordCoord, OwnView>::AccessibleRecordDim>
1018 {
1019 };
1020 
1022 template<std::size_t I, typename View, typename BoundRecordCoord, bool OwnView>
1023 struct std::tuple_element<I, llama::RecordRef<View, BoundRecordCoord, OwnView>> // NOLINT(cert-dcl58-cpp)
1024 {
1025  using type = decltype(std::declval<llama::RecordRef<View, BoundRecordCoord, OwnView>>().template get<I>());
1026 };
1027 
1029 template<std::size_t I, typename View, typename BoundRecordCoord, bool OwnView>
1030 struct std::tuple_element<I, const llama::RecordRef<View, BoundRecordCoord, OwnView>> // NOLINT(cert-dcl58-cpp)
1031 {
1032  using type = decltype(std::declval<const llama::RecordRef<View, BoundRecordCoord, OwnView>>().template get<I>());
1033 };
1034 
1036 template<typename View, typename BoundRecordCoord, bool OwnView>
1037 struct std::hash<llama::RecordRef<View, BoundRecordCoord, OwnView>> // NOLINT(cert-dcl58-cpp)
1038 {
1040  -> std::size_t
1041  {
1042  std::size_t acc = 0;
1044  rr,
1045  [&](auto&& ref) LLAMA_LAMBDA_INLINE { boost::hash_combine(acc, llama::decayCopy(ref)); });
1046  return acc;
1047  }
1048 };
1049 
1050 #if CAN_USE_RANGES
1052 template<
1053  typename ViewA,
1054  typename BoundA,
1055  bool OwnA,
1056  typename ViewB,
1057  typename BoundB,
1058  bool OwnB,
1059  template<class>
1060  class TQual,
1061  template<class>
1062  class UQual>
1063 struct std::
1064  // NOLINTNEXTLINE(cert-dcl58-cpp)
1065  basic_common_reference<llama::RecordRef<ViewA, BoundA, OwnA>, llama::RecordRef<ViewB, BoundB, OwnB>, TQual, UQual>
1066 {
1067  using type = std::enable_if_t<
1068  std::is_same_v<
1072 };
1073 #endif
#define LLAMA_EXPORT
Definition: macros.hpp:192
#define LLAMA_LAMBDA_INLINE_WITH_SPECIFIERS(...)
Definition: macros.hpp:107
#define LLAMA_LAMBDA_INLINE
Gives strong indication to the compiler to inline the attributed lambda.
Definition: macros.hpp:113
#define LLAMA_BEGIN_SUPPRESS_HOST_DEVICE_WARNING
Definition: macros.hpp:141
#define LLAMA_FN_HOST_ACC_INLINE
Definition: macros.hpp:96
#define LLAMA_END_SUPPRESS_HOST_DEVICE_WARNING
Definition: macros.hpp:153
void loadSimdFromField(const T &srcRef, Simd &dstSimd, SrcRC srcRC, DstRC dstRC)
Definition: Simd.hpp:209
auto makeFromTuple(Tuple &&src, std::index_sequence< Is... >)
Definition: RecordRef.hpp:307
auto asFlatTupleImplForArray(RecordRef &&vd, T(&&)[N], std::index_sequence< Is... >)
Definition: RecordRef.hpp:250
auto recordRefArithOperator(LeftRecord &left, const RecordRef< RightView, RightBoundRecordDim, RightOwnView > &right) -> LeftRecord &
Definition: RecordRef.hpp:49
void assignTupleElement(T1 &&dst, T2 &&src)
Definition: RecordRef.hpp:283
auto asFlatTupleImpl(ProxyReference &&leaf, T) -> std::tuple< ProxyReference >
Definition: RecordRef.hpp:234
void storeSimdToField(const Simd &srcSimd, TFwd &&dstRef, SrcRC srcRC, DstRC dstRC)
Definition: Simd.hpp:255
auto asTupleImpl(ProxyReference &&leaf, T) -> ProxyReference
Definition: RecordRef.hpp:197
constexpr auto dependentFalse
Definition: RecordRef.hpp:277
constexpr auto isTupleLike
Definition: RecordRef.hpp:268
void assignTuples(Tuple1 &&dst, Tuple2 &&src, std::index_sequence< Is... >)
Definition: RecordRef.hpp:299
constexpr auto isDirectListInitializable
Definition: RecordRef.hpp:322
constexpr auto isDirectListInitializableFromTuple
Definition: RecordRef.hpp:325
constexpr auto isDirectListInitializableImpl
Definition: RecordRef.hpp:314
auto asTupleImplForArray(RecordRef &&vd, T(&&)[N], std::index_sequence< Is... >)
Definition: RecordRef.hpp:213
auto recordRefRelOperator(const LeftRecord &left, const RecordRef< RightView, RightBoundRecordDim, RightOwnView > &right) -> bool
Definition: RecordRef.hpp:96
auto decayCopy(T &&valueOrRef) -> typename internal::ValueOf< T >::type
Pulls a copy of the given value or reference. Proxy references are resolved to their value types.
Definition: RecordRef.hpp:877
Tuple(Elements...) -> Tuple< std::remove_cv_t< std::remove_reference_t< Elements >>... >
auto allocScalarView() -> decltype(auto)
Definition: View.hpp:174
auto copyRecord(const RecordRef< View, BoundRecordCoord, OwnView > &rr)
Returns a One with the same record dimension as the given record ref, with values copyied from rr.
Definition: RecordRef.hpp:33
auto operator<<(std::ostream &os, const Array< T, N > &a) -> std::ostream &
Definition: Array.hpp:249
mp_second< Field > GetFieldType
Get the type from a Field.
Definition: Core.hpp:191
constexpr void forEachLeaf(RecordRefFwd &&vr, Functor &&functor)
Definition: RecordRef.hpp:833
constexpr auto get(Tuple< Elements... > &tuple) -> auto &
Definition: Tuple.hpp:121
typename internal::GetCoordFromTagsImpl< RecordDim, RecordCoord<>, TagsOrTagList... >::type GetCoordFromTags
Definition: Core.hpp:353
mp_first< Field > GetFieldTag
Get the tag from a Field.
Definition: Core.hpp:186
RecordCoordFromList< mp_append< typename RecordCoords::List... > > Cat
Concatenate a set of RecordCoords.
typename internal::GetTypeImpl< RecordDim, RecordCoordOrTags... >::type GetType
Definition: Core.hpp:388
ScopedUpdate(T) -> ScopedUpdate< typename internal::ReferenceTo< std::remove_reference_t< T >>::type >
typename std::conditional_t< isRecordDim< T >, mp_identity< One< Simdize< T, MakeSimd > >>, mp_identity< Simdize< T, MakeSimd > >>::type Simd
Definition: Simd.hpp:169
auto swap(RecordRef< ViewA, BoundRecordDimA, OwnViewA > &a, RecordRef< ViewB, BoundRecordDimB, OwnViewB > &b) noexcept -> std::enable_if_t< std::is_same_v< typename RecordRef< ViewA, BoundRecordDimA, OwnViewA >::AccessibleRecordDim, typename RecordRef< ViewB, BoundRecordDimB, OwnViewB >::AccessibleRecordDim >>
Definition: RecordRef.hpp:782
constexpr auto isRecordRef
Definition: RecordRef.hpp:24
CRTP mixin for proxy reference types to support all compound assignment and increment/decrement opera...
friend auto operator>=(const RecordRef &vd, const T &t) -> bool
Definition: RecordRef.hpp:629
auto operator()(Tags...) -> decltype(auto)
Definition: RecordRef.hpp:464
auto get() const -> decltype(auto)
Definition: RecordRef.hpp:667
auto operator()(RecordCoord< Coord... >) -> decltype(auto)
Definition: RecordRef.hpp:442
friend auto operator>=(const T &t, const RecordRef &vd) -> bool
Definition: RecordRef.hpp:635
friend auto operator>(const T &t, const RecordRef &vd) -> bool
Definition: RecordRef.hpp:623
auto operator=(const RecordRef &other) -> RecordRef &
Definition: RecordRef.hpp:381
friend auto operator<=(const RecordRef &vd, const T &t) -> bool
Definition: RecordRef.hpp:605
auto operator*=(const T &other) -> RecordRef &
Definition: RecordRef.hpp:509
friend auto operator*(const RecordRef &vd, const T &t)
Definition: RecordRef.hpp:545
GetType< RecordDim, BoundRecordCoord > AccessibleRecordDim
Definition: RecordRef.hpp:360
friend auto operator==(const T &t, const RecordRef &vd) -> bool
Definition: RecordRef.hpp:575
auto operator()(Tags...) const -> decltype(auto)
Definition: RecordRef.hpp:456
friend auto operator!=(const T &t, const RecordRef &vd) -> bool
Definition: RecordRef.hpp:587
auto operator=(const T &other) -> RecordRef &
Definition: RecordRef.hpp:490
friend auto operator<(const T &t, const RecordRef &vd) -> bool
Definition: RecordRef.hpp:599
friend auto operator!=(const RecordRef &vd, const T &t) -> bool
Definition: RecordRef.hpp:581
auto asTuple() const
Definition: RecordRef.hpp:645
friend auto operator-(const RecordRef &vd, const T &t)
Definition: RecordRef.hpp:539
friend auto operator>(const RecordRef &vd, const T &t) -> bool
Definition: RecordRef.hpp:617
auto operator/=(const T &other) -> RecordRef &
Definition: RecordRef.hpp:515
friend auto operator<(const RecordRef &vd, const T &t) -> bool
Definition: RecordRef.hpp:593
friend auto operator+(const T &t, const RecordRef &vd)
Definition: RecordRef.hpp:533
RecordRef(ArrayIndex ai, std::conditional_t< OwnView, View &&, View & > view)
Definition: RecordRef.hpp:372
auto operator()(RecordCoord< Coord... >) const -> decltype(auto)
Definition: RecordRef.hpp:430
auto get() -> decltype(auto)
Definition: RecordRef.hpp:661
RecordRef(const RecordRef &)=default
auto load() -> Loader
Definition: RecordRef.hpp:722
friend auto operator%(const RecordRef &vd, const T &t)
Definition: RecordRef.hpp:563
auto operator+=(const T &other) -> RecordRef &
Definition: RecordRef.hpp:497
auto loadAs() -> TupleLike
Definition: RecordRef.hpp:673
auto asFlatTuple() const
Definition: RecordRef.hpp:655
friend auto operator==(const RecordRef &vd, const T &t) -> bool
Definition: RecordRef.hpp:569
auto operator%=(const T &other) -> RecordRef &
Definition: RecordRef.hpp:521
auto loadAs() const -> TupleLike
Definition: RecordRef.hpp:686
TBoundRecordCoord BoundRecordCoord
Record coords into View::RecordDim which are already bound by this RecordRef.
Definition: RecordRef.hpp:349
TView View
View this record reference points into.
Definition: RecordRef.hpp:347
void store(const TupleLike &t)
Definition: RecordRef.hpp:733
RecordRef(RecordRef &&) noexcept=default
constexpr auto arrayIndex() const -> ArrayIndex
Definition: RecordRef.hpp:392
friend auto operator+(const RecordRef &vd, const T &t)
Definition: RecordRef.hpp:527
friend auto operator*(const T &t, const RecordRef &vd)
Definition: RecordRef.hpp:551
friend void swap(std::conditional_t< OwnView, RecordRef &, RecordRef > a, std::conditional_t< OwnView, RecordRef &, RecordRef > b) noexcept
Definition: RecordRef.hpp:739
friend auto operator/(const RecordRef &vd, const T &t)
Definition: RecordRef.hpp:557
RecordRef(const T &scalar)
Create a RecordRef from a scalar. Only available for if the view is owned. Used by llama::One.
Definition: RecordRef.hpp:416
friend auto operator<=(const T &t, const RecordRef &vd) -> bool
Definition: RecordRef.hpp:611
auto operator-=(const T &other) -> RecordRef &
Definition: RecordRef.hpp:503
RecordRef(const RecordRef< OtherView, OtherBoundRecordCoord, OtherOwnView > &recordRef)
Definition: RecordRef.hpp:401
RecordRef()
Creates an empty RecordRef. Only available for if the view is owned. Used by llama::One.
Definition: RecordRef.hpp:363
auto load() const -> LoaderConst
Definition: RecordRef.hpp:727
A type list of Fields which may be used to define a record dimension.
Definition: Core.hpp:61
ScopedUpdate(const ScopedUpdate &)=delete
auto operator=(const ScopedUpdate &) -> ScopedUpdate &=delete
auto get() -> value_type &
Get access to the stored value.
Definition: RecordRef.hpp:912
auto get() const -> const value_type &
Get access to the stored value.
Definition: RecordRef.hpp:918
ScopedUpdate(Reference r)
Loads a copy of the value referenced by r. Stores r and the loaded value.
Definition: RecordRef.hpp:893
ScopedUpdate(ScopedUpdate &&) noexcept=default
typename internal::ValueOf< Reference >::type value_type
Definition: RecordRef.hpp:890
auto operator()(A &&a, const B &b) const -> decltype(auto)
Definition: RecordRef.hpp:142
auto operator()(A &&a, const B &b) const -> decltype(auto)
Definition: RecordRef.hpp:178
auto operator()(A &&a, const B &b) const -> decltype(auto)
Definition: RecordRef.hpp:160
auto operator()(A &&a, const B &b) const -> decltype(auto)
Definition: RecordRef.hpp:187
auto operator()(A &&a, const B &b) const -> decltype(auto)
Definition: RecordRef.hpp:169
auto operator()(A &&a, const B &b) const -> decltype(auto)
Definition: RecordRef.hpp:151
std::remove_const_t< T > type
Definition: RecordRef.hpp:870
auto operator()(const llama::RecordRef< View, BoundRecordCoord, OwnView > &rr) const -> std::size_t
Definition: RecordRef.hpp:1039
decltype(std::declval< const llama::RecordRef< View, BoundRecordCoord, OwnView > >().template get< I >()) type
Definition: RecordRef.hpp:1032
decltype(std::declval< llama::RecordRef< View, BoundRecordCoord, OwnView > >().template get< I >()) type
Definition: RecordRef.hpp:1025