Low-Level Abstraction of Memory Access
ArrayExtents.hpp
Go to the documentation of this file.
1 // Copyright 2022 Bernhard Manfred Gruber
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #pragma once
5 
6 #include "Array.hpp"
7 #include "Meta.hpp"
8 
9 #include <limits>
10 #include <type_traits>
11 
12 namespace llama
13 {
14  // TODO(bgruber): make this an alias in C++20, when we have CTAD for aliases
18  template<typename T, std::size_t Dim>
19  struct ArrayIndex : Array<T, Dim>
20  {
21  static constexpr std::size_t rank = Dim;
22  };
23 
24  // allow comparing ArrayIndex with different size types:
26  template<std::size_t Dim, typename TA, typename TB>
28  {
29  for(std::size_t i = 0; i < Dim; ++i)
30  if(a[i] != b[i])
31  return false;
32  return true;
33  }
34 
36  template<std::size_t Dim, typename TA, typename TB>
38  {
39  return !(a == b);
40  }
41 
42  static_assert(
43  std::is_trivially_default_constructible_v<ArrayIndex<int, 1>>); // so ArrayIndex<1>{} will produce a zeroed
44  // index. Should hold for all dimensions,
45  // but just checking for <1> here.
46  static_assert(std::is_trivially_copy_constructible_v<ArrayIndex<int, 1>>);
47  static_assert(std::is_trivially_move_constructible_v<ArrayIndex<int, 1>>);
48  static_assert(std::is_trivially_copy_assignable_v<ArrayIndex<int, 1>>);
49  static_assert(std::is_trivially_move_assignable_v<ArrayIndex<int, 1>>);
50 
51  namespace internal
52  {
53  template<typename Default, typename... Ints>
55  {
56  using type = Default;
57  };
58 
59  template<typename Default, typename FirstInt, typename... Ints>
60  struct IndexTypeFromArgs<Default, FirstInt, Ints...>
61  {
62  static_assert(std::conjunction_v<std::is_same<FirstInt, Ints>...>, "All index types must be the same");
63  using type = FirstInt;
64  };
65  } // namespace internal
66 
68  template<typename... Args>
69  ArrayIndex(Args...)
70  -> ArrayIndex<typename internal::IndexTypeFromArgs<std::size_t, Args...>::type, sizeof...(Args)>;
71 } // namespace llama
72 
74 template<typename V, size_t N>
75 struct std::tuple_size<llama::ArrayIndex<V, N>> : std::integral_constant<size_t, N> // NOLINT(cert-dcl58-cpp)
76 {
77 };
78 
80 template<size_t I, typename V, size_t N>
81 struct std::tuple_element<I, llama::ArrayIndex<V, N>> // NOLINT(cert-dcl58-cpp)
82 {
83  using type = V;
84 };
85 
86 namespace llama
87 {
88  namespace internal
89  {
90  struct Dyn
91  {
92  template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
93  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
94  LLAMA_FN_HOST_ACC_INLINE constexpr operator T() const
95  {
96  return static_cast<T>(-1);
97  }
98 
99  template<typename T>
100  LLAMA_FN_HOST_ACC_INLINE friend constexpr auto operator==(T i, Dyn) -> bool
101  {
102  return i == static_cast<T>(-1);
103  }
104 
105  template<typename T>
106  LLAMA_FN_HOST_ACC_INLINE friend constexpr auto operator==(Dyn d, T i) -> bool
107  {
108  return i == d;
109  }
110 
111  template<typename T>
112  LLAMA_FN_HOST_ACC_INLINE friend constexpr auto operator!=(T i, Dyn d) -> bool
113  {
114  return !(i == d);
115  }
116 
117  template<typename T>
118  LLAMA_FN_HOST_ACC_INLINE friend constexpr auto operator!=(Dyn d, T i) -> bool
119  {
120  return !(i == d);
121  }
122  };
123  } // namespace internal
124 
127  inline constexpr auto dyn = internal::Dyn{};
128 
133  template<typename T = std::size_t, T... Sizes>
134  struct ArrayExtents : Array<T, ((Sizes == dyn) + ... + 0)>
135  {
136  static constexpr std::size_t rank = sizeof...(Sizes);
137  static constexpr auto rankDynamic = ((Sizes == dyn) + ... + 0);
138  static constexpr auto rankStatic = rank - rankDynamic;
139 
141  using value_type = T;
142 
143  template<std::size_t I>
144  LLAMA_FN_HOST_ACC_INLINE constexpr auto get() const -> value_type
145  {
146  using TypeList = mp_list_c<T, Sizes...>;
147  constexpr auto extent = mp_at_c<TypeList, I>::value;
148  if constexpr(extent != dyn)
149  return extent;
150  else
151  return static_cast<const Array<value_type, rankDynamic>&>(
152  *this)[+mp_count<mp_take_c<TypeList, I>, std::integral_constant<T, dyn>>::value];
153  }
154 
155  LLAMA_FN_HOST_ACC_INLINE constexpr auto operator[](T i) const -> value_type
156  {
157  return mp_with_index<rank>(i, [&](auto ic) LLAMA_LAMBDA_INLINE { return get<decltype(ic)::value>(); });
158  }
159 
160  private:
161  template<std::size_t... Is>
162  LLAMA_FN_HOST_ACC_INLINE constexpr auto toArray(std::index_sequence<Is...>) const -> Index
163  {
164  return {get<Is>()...};
165  }
166 
167  public:
168  LLAMA_FN_HOST_ACC_INLINE constexpr auto toArray() const -> Index
169  {
170  return toArray(std::make_index_sequence<rank>{});
171  }
172  };
173 
175  template<typename T>
176  struct ArrayExtents<T>
177  {
178  static constexpr std::size_t rank = 0;
179  static constexpr auto rankDynamic = 0;
180  static constexpr auto rankStatic = 0;
181 
183  using value_type = T;
184 
185  LLAMA_FN_HOST_ACC_INLINE constexpr auto toArray() const -> Index
186  {
187  return {};
188  }
189  };
190 
192  template<typename... Args>
194  typename internal::IndexTypeFromArgs<std::size_t, Args...>::type,
195  (Args{}, dyn)...>; // we just use Args to repeat dyn as often as Args is big
196 
197  static_assert(std::is_trivially_default_constructible_v<ArrayExtents<std::size_t, 1>>);
198  static_assert(std::is_trivially_copy_constructible_v<ArrayExtents<std::size_t, 1>>);
199  static_assert(std::is_trivially_move_constructible_v<ArrayExtents<std::size_t, 1>>);
200  static_assert(std::is_trivially_copy_assignable_v<ArrayExtents<std::size_t, 1>>);
201  static_assert(std::is_trivially_move_assignable_v<ArrayExtents<std::size_t, 1>>);
202  static_assert(std::is_empty_v<ArrayExtents<std::size_t, 1>>);
203 
205  template<typename SizeTypeA, SizeTypeA... SizesA, typename SizeTypeB, SizeTypeB... SizesB>
209  {
210  return a.toArray() == b.toArray();
211  }
212 
214  template<typename SizeTypeA, SizeTypeA... SizesA, typename SizeTypeB, SizeTypeB... SizesB>
218  {
219  return !(a == b);
220  }
221 
223  template<typename SizeType, SizeType... Sizes>
225  {
226  return product(e.toArray());
227  }
228 
229  namespace internal
230  {
231  template<typename SizeType, SizeType Extent, std::size_t... Is>
232  constexpr auto makeArrayExtents(std::index_sequence<Is...>)
233  {
234  return ArrayExtents<SizeType, (static_cast<void>(Is), Extent)...>{};
235  }
236  } // namespace internal
237 
240  template<typename SizeType, std::size_t N, SizeType Extent>
241  using ArrayExtentsNCube = decltype(internal::makeArrayExtents<SizeType, Extent>(std::make_index_sequence<N>{}));
242 
245  template<typename SizeType, std::size_t N>
247 
249  template<typename SizeType, std::size_t Dim, typename Func, typename... OuterIndices>
251  [[maybe_unused]] const ArrayIndex<SizeType, Dim>& extents,
252  Func&& func,
253  OuterIndices... outerIndices)
254  {
255  constexpr auto fixedIndices = sizeof...(outerIndices);
257  if constexpr(fixedIndices < Dim)
258  {
259  for(SizeType i = 0; i < extents[fixedIndices]; i++)
260  forEachArrayIndex(extents, std::forward<Func>(func), outerIndices..., i);
261  }
262  else
263  {
264  std::forward<Func>(func)(ArrayIndex<SizeType, fixedIndices>{outerIndices...});
265  }
267  }
268 
270  template<typename SizeType, SizeType... Sizes, typename Func>
272  {
273  forEachArrayIndex(extents.toArray(), std::forward<Func>(func));
274  }
275 } // namespace llama
276 
278 template<typename SizeType, SizeType... Sizes>
279 struct std::tuple_size<llama::ArrayExtents<SizeType, Sizes...>> // NOLINT(cert-dcl58-cpp)
280  : std::integral_constant<std::size_t, sizeof...(Sizes)>
281 {
282 };
283 
285 template<typename SizeType, std::size_t I, SizeType... Sizes>
286 struct std::tuple_element<I, llama::ArrayExtents<SizeType, Sizes...>> // NOLINT(cert-dcl58-cpp)
287 {
288  using type = SizeType;
289 };
#define LLAMA_EXPORT
Definition: macros.hpp:192
#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
constexpr auto makeArrayExtents(std::index_sequence< Is... >)
ArrayIndex(Args...) -> ArrayIndex< typename internal::IndexTypeFromArgs< std::size_t, Args... >::type, sizeof...(Args)>
void forEachArrayIndex([[maybe_unused]] const ArrayIndex< SizeType, Dim > &extents, Func &&func, OuterIndices... outerIndices)
constexpr auto dyn
Used as a template argument to ArrayExtents to mark a dynamic extent.
ArrayExtentsNCube< SizeType, N, dyn > ArrayExtentsDynamic
N-dimensional ArrayExtents where all values are dynamic.
ArrayExtents(Args...) -> ArrayExtents< typename internal::IndexTypeFromArgs< std::size_t, Args... >::type,(Args{}, dyn)... >
decltype(internal::makeArrayExtents< SizeType, Extent >(std::make_index_sequence< N >{})) ArrayExtentsNCube
N-dimensional ArrayExtents where all N extents are Extent.
constexpr auto operator==(ArrayIndex< TA, Dim > a, ArrayIndex< TB, Dim > b) -> bool
constexpr auto operator!=(ArrayIndex< TA, Dim > a, ArrayIndex< TB, Dim > b) -> bool
constexpr auto product(Array< T, N > a) -> T
Definition: Array.hpp:315
constexpr auto toArray() const -> Index
constexpr auto get() const -> value_type
static constexpr auto rankStatic
constexpr auto toArray() const -> Index
static constexpr auto rankDynamic
static constexpr std::size_t rank
ArrayIndex< T, rank > Index
constexpr auto operator[](T i) const -> value_type
static constexpr std::size_t rank
constexpr friend auto operator==(Dyn d, T i) -> bool
constexpr friend auto operator==(T i, Dyn) -> bool
constexpr friend auto operator!=(T i, Dyn d) -> bool
constexpr friend auto operator!=(Dyn d, T i) -> bool