OpenKalman
extents.hpp
1 //@HEADER
2 // ************************************************************************
3 //
4 // Kokkos v. 4.0
5 // Copyright (2022) National Technology & Engineering
6 // Solutions of Sandia, LLC (NTESS).
7 //
8 // Under the terms of Contract DE-NA0003525 with NTESS,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12 // See https://kokkos.org/LICENSE for license information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //@HEADER
16 
17 #pragma once
18 #include "dynamic_extent.hpp"
19 
20 #ifdef __cpp_lib_span
21 #include <span>
22 #endif
23 #include <array>
24 
25 #include <cinttypes>
26 
27 namespace std {
28 namespace experimental {
29 namespace detail {
30 
31 // Function used to check compatibility of extents in converting constructor
32 // can't be a private member function for some reason.
33 template <size_t... Extents, size_t... OtherExtents>
34 static constexpr std::integral_constant<bool, false> __check_compatible_extents(
35  std::integral_constant<bool, false>,
38  return {};
39 }
40 
41 // This helper prevents ICE's on MSVC.
42 template <size_t Lhs, size_t Rhs>
43 struct __compare_extent_compatible : std::integral_constant<bool,
44  Lhs == dynamic_extent ||
45  Rhs == dynamic_extent ||
46  Lhs == Rhs>
47 {};
48 
49 template <size_t... Extents, size_t... OtherExtents>
50 static constexpr std::integral_constant<
52 __check_compatible_extents(
53  std::integral_constant<bool, true>,
56  return {};
57 }
58 
59 // ------------------------------------------------------------------
60 // ------------ static_array ----------------------------------------
61 // ------------------------------------------------------------------
62 
63 // array like class which provides an array of static values with get
64 // function and operator [].
65 
66 // Implementation of Static Array with recursive implementation of get.
67 template <size_t R, class T, T... Extents> struct static_array_impl;
68 
69 template <size_t R, class T, T FirstExt, T... Extents>
70 struct static_array_impl<R, T, FirstExt, Extents...> {
71  MDSPAN_INLINE_FUNCTION
72  constexpr static T get(size_t r) {
73  if (r == R)
74  return FirstExt;
75  else
77  }
78  template <size_t r> MDSPAN_INLINE_FUNCTION constexpr static T get() {
79 #if MDSPAN_HAS_CXX_17
80  if constexpr (r == R)
81  return FirstExt;
82  else
83  return static_array_impl<R + 1, T, Extents...>::template get<r>();
84 #else
85  get(r);
86 #endif
87  }
88 };
89 
90 // End the recursion
91 template <size_t R, class T, T FirstExt>
92 struct static_array_impl<R, T, FirstExt> {
93  MDSPAN_INLINE_FUNCTION
94  constexpr static T get(size_t) { return FirstExt; }
95  template <size_t> MDSPAN_INLINE_FUNCTION constexpr static T get() {
96  return FirstExt;
97  }
98 };
99 
100 // Don't start recursion if size 0
101 template <class T> struct static_array_impl<0, T> {
102  MDSPAN_INLINE_FUNCTION
103  constexpr static T get(size_t) { return T(); }
104  template <size_t> MDSPAN_INLINE_FUNCTION constexpr static T get() {
105  return T();
106  }
107 };
108 
109 // Static array, provides get<r>(), get(r) and operator[r]
110 template <class T, T... Values> struct static_array:
111  public static_array_impl<0, T, Values...> {
112 
113 public:
114  using value_type = T;
115 
116  MDSPAN_INLINE_FUNCTION
117  constexpr static size_t size() { return sizeof...(Values); }
118 };
119 
120 
121 // ------------------------------------------------------------------
122 // ------------ index_sequence_scan ---------------------------------
123 // ------------------------------------------------------------------
124 
125 // index_sequence_scan takes compile time values and provides get(r)
126 // and get<r>() which return the sum of the first r-1 values.
127 
128 // Recursive implementation for get
129 template <size_t R, size_t... Values> struct index_sequence_scan_impl;
130 
131 template <size_t R, size_t FirstVal, size_t... Values>
132 struct index_sequence_scan_impl<R, FirstVal, Values...> {
133  MDSPAN_INLINE_FUNCTION
134  constexpr static size_t get(size_t r) {
135  if (r > R)
137  else
138  return 0;
139  }
140 };
141 
142 template <size_t R, size_t FirstVal>
143 struct index_sequence_scan_impl<R, FirstVal> {
144 #if defined(__NVCC__) || defined(__NVCOMPILER)
145  // NVCC warns about pointless comparison with 0 for R==0 and r being const
146  // evaluatable and also 0.
147  MDSPAN_INLINE_FUNCTION
148  constexpr static size_t get(size_t r) {
149  return static_cast<int64_t>(R) > static_cast<int64_t>(r) ? FirstVal : 0;
150  }
151 #else
152  MDSPAN_INLINE_FUNCTION
153  constexpr static size_t get(size_t r) { return R > r ? FirstVal : 0; }
154 #endif
155 };
156 template <> struct index_sequence_scan_impl<0> {
157  MDSPAN_INLINE_FUNCTION
158  constexpr static size_t get(size_t) { return 0; }
159 };
160 
161 // ------------------------------------------------------------------
162 // ------------ possibly_empty_array -------------------------------
163 // ------------------------------------------------------------------
164 
165 // array like class which provides get function and operator [], and
166 // has a specialization for the size 0 case.
167 // This is needed to make the maybe_static_array be truly empty, for
168 // all static values.
169 
170 template <class T, size_t N> struct possibly_empty_array {
171  T vals[N];
172  MDSPAN_INLINE_FUNCTION
173  constexpr T &operator[](size_t r) { return vals[r]; }
174  MDSPAN_INLINE_FUNCTION
175  constexpr const T &operator[](size_t r) const { return vals[r]; }
176 };
177 
178 template <class T> struct possibly_empty_array<T, 0> {
179  MDSPAN_INLINE_FUNCTION
180  constexpr T operator[](size_t) { return T(); }
181  MDSPAN_INLINE_FUNCTION
182  constexpr const T operator[](size_t) const { return T(); }
183 };
184 
185 // ------------------------------------------------------------------
186 // ------------ maybe_static_array ----------------------------------
187 // ------------------------------------------------------------------
188 
189 // array like class which has a mix of static and runtime values but
190 // only stores the runtime values.
191 // The type of the static and the runtime values can be different.
192 // The position of a dynamic value is indicated through a tag value.
193 template <class TDynamic, class TStatic, TStatic dyn_tag, TStatic... Values>
195 
196  static_assert(is_convertible<TStatic, TDynamic>::value, "maybe_static_array: TStatic must be convertible to TDynamic");
197  static_assert(is_convertible<TDynamic, TStatic>::value, "maybe_static_array: TDynamic must be convertible to TStatic");
198 
199 private:
200  // Static values member
201  using static_vals_t = static_array<TStatic, Values...>;
202  constexpr static size_t m_size = sizeof...(Values);
203  constexpr static size_t m_size_dynamic =
204  _MDSPAN_FOLD_PLUS_RIGHT((Values == dyn_tag), 0);
205 
206  // Dynamic values member
207  _MDSPAN_NO_UNIQUE_ADDRESS possibly_empty_array<TDynamic, m_size_dynamic>
208  m_dyn_vals;
209 
210  // static mapping of indices to the position in the dynamic values array
211  using dyn_map_t = index_sequence_scan_impl<0, static_cast<size_t>(Values == dyn_tag)...>;
212 public:
213 
214  // two types for static and dynamic values
215  using value_type = TDynamic;
216  using static_value_type = TStatic;
217  // tag value indicating dynamic value
218  constexpr static static_value_type tag_value = dyn_tag;
219 
220  constexpr maybe_static_array() = default;
221 
222  // constructor for all static values
223  // TODO: add precondition check?
224  MDSPAN_TEMPLATE_REQUIRES(class... Vals,
225  /* requires */ ((m_size_dynamic == 0) &&
226  (sizeof...(Vals) > 0)))
227  MDSPAN_INLINE_FUNCTION
228  constexpr maybe_static_array(Vals...) : m_dyn_vals{} {}
229 
230  // constructors from dynamic values only
231  MDSPAN_TEMPLATE_REQUIRES(class... DynVals,
232  /* requires */ (sizeof...(DynVals) ==
233  m_size_dynamic &&
234  m_size_dynamic > 0))
235  MDSPAN_INLINE_FUNCTION
236  constexpr maybe_static_array(DynVals... vals)
237  : m_dyn_vals{static_cast<TDynamic>(vals)...} {}
238 
239 
240  MDSPAN_TEMPLATE_REQUIRES(class T, size_t N,
241  /* requires */ (N == m_size_dynamic && N > 0))
242  MDSPAN_INLINE_FUNCTION
243  constexpr maybe_static_array(const std::array<T, N> &vals) {
244  for (size_t r = 0; r < N; r++)
245  m_dyn_vals[r] = static_cast<TDynamic>(vals[r]);
246  }
247 
248  MDSPAN_TEMPLATE_REQUIRES(class T, size_t N,
249  /* requires */ (N == m_size_dynamic && N == 0))
250  MDSPAN_INLINE_FUNCTION
251  constexpr maybe_static_array(const std::array<T, N> &) : m_dyn_vals{} {}
252 
253 #ifdef __cpp_lib_span
254  MDSPAN_TEMPLATE_REQUIRES(class T, size_t N,
255  /* requires */ (N == m_size_dynamic))
256  MDSPAN_INLINE_FUNCTION
257  constexpr maybe_static_array(const std::span<T, N> &vals) {
258  for (size_t r = 0; r < N; r++)
259  m_dyn_vals[r] = static_cast<TDynamic>(vals[r]);
260  }
261 #endif
262 
263  // constructors from all values
264  MDSPAN_TEMPLATE_REQUIRES(class... DynVals,
265  /* requires */ (sizeof...(DynVals) !=
266  m_size_dynamic &&
267  m_size_dynamic > 0))
268  MDSPAN_INLINE_FUNCTION
269  constexpr maybe_static_array(DynVals... vals) {
270  static_assert((sizeof...(DynVals) == m_size), "Invalid number of values.");
271  TDynamic values[m_size]{static_cast<TDynamic>(vals)...};
272  for (size_t r = 0; r < m_size; r++) {
273  TStatic static_val = static_vals_t::get(r);
274  if (static_val == dyn_tag) {
275  m_dyn_vals[dyn_map_t::get(r)] = values[r];
276  }
277 // Precondition check
278 #ifdef _MDSPAN_DEBUG
279  else {
280  assert(values[r] == static_cast<TDynamic>(static_val));
281  }
282 #endif
283  }
284  }
285 
286  MDSPAN_TEMPLATE_REQUIRES(
287  class T, size_t N,
288  /* requires */ (N != m_size_dynamic && m_size_dynamic > 0))
289  MDSPAN_INLINE_FUNCTION
290  constexpr maybe_static_array(const std::array<T, N> &vals) {
291  static_assert((N == m_size), "Invalid number of values.");
292 // Precondition check
293 #ifdef _MDSPAN_DEBUG
294  assert(N == m_size);
295 #endif
296  for (size_t r = 0; r < m_size; r++) {
297  TStatic static_val = static_vals_t::get(r);
298  if (static_val == dyn_tag) {
299  m_dyn_vals[dyn_map_t::get(r)] = static_cast<TDynamic>(vals[r]);
300  }
301 // Precondition check
302 #ifdef _MDSPAN_DEBUG
303  else {
304  assert(static_cast<TDynamic>(vals[r]) ==
305  static_cast<TDynamic>(static_val));
306  }
307 #endif
308  }
309  }
310 
311 #ifdef __cpp_lib_span
312  MDSPAN_TEMPLATE_REQUIRES(
313  class T, size_t N,
314  /* requires */ (N != m_size_dynamic && m_size_dynamic > 0))
315  MDSPAN_INLINE_FUNCTION
316  constexpr maybe_static_array(const std::span<T, N> &vals) {
317  static_assert((N == m_size) || (m_size == dynamic_extent));
318 #ifdef _MDSPAN_DEBUG
319  assert(N == m_size);
320 #endif
321  for (size_t r = 0; r < m_size; r++) {
322  TStatic static_val = static_vals_t::get(r);
323  if (static_val == dyn_tag) {
324  m_dyn_vals[dyn_map_t::get(r)] = static_cast<TDynamic>(vals[r]);
325  }
326 #ifdef _MDSPAN_DEBUG
327  else {
328  assert(static_cast<TDynamic>(vals[r]) ==
329  static_cast<TDynamic>(static_val));
330  }
331 #endif
332  }
333  }
334 #endif
335 
336  // access functions
337  MDSPAN_INLINE_FUNCTION
338  constexpr static TStatic static_value(size_t r) { return static_vals_t::get(r); }
339 
340  MDSPAN_INLINE_FUNCTION
341  constexpr TDynamic value(size_t r) const {
342  TStatic static_val = static_vals_t::get(r);
343  return static_val == dyn_tag ? m_dyn_vals[dyn_map_t::get(r)]
344  : static_cast<TDynamic>(static_val);
345  }
346  MDSPAN_INLINE_FUNCTION
347  constexpr TDynamic operator[](size_t r) const { return value(r); }
348 
349 
350  // observers
351  MDSPAN_INLINE_FUNCTION
352  constexpr static size_t size() { return m_size; }
353  MDSPAN_INLINE_FUNCTION
354  constexpr static size_t size_dynamic() { return m_size_dynamic; }
355 };
356 
357 } // namespace detail
358 } // namespace experimental
359 } // namespace std
360 
361 namespace std {
362 namespace experimental {
363 
364 // ------------------------------------------------------------------
365 // ------------ extents ---------------------------------------------
366 // ------------------------------------------------------------------
367 
368 // Class to describe the extents of a multi dimensional array.
369 // Used by mdspan, mdarray and layout mappings.
370 // See ISO C++ standard [mdspan.extents]
371 
372 template <class IndexType, size_t... Extents> class extents {
373 public:
374  // typedefs for integral types used
375  using index_type = IndexType;
376  using size_type = make_unsigned_t<index_type>;
377  using rank_type = size_t;
378 
380  "extents::index_type must be a signed or unsigned integer type");
381 private:
382  constexpr static rank_type m_rank = sizeof...(Extents);
383  constexpr static rank_type m_rank_dynamic =
384  _MDSPAN_FOLD_PLUS_RIGHT((Extents == dynamic_extent), /* + ... + */ 0);
385 
386  // internal storage type using maybe_static_array
387  using vals_t =
388  detail::maybe_static_array<IndexType, size_t, dynamic_extent, Extents...>;
389  _MDSPAN_NO_UNIQUE_ADDRESS vals_t m_vals;
390 
391 public:
392  // [mdspan.extents.obs], observers of multidimensional index space
393  MDSPAN_INLINE_FUNCTION
394  constexpr static rank_type rank() noexcept { return m_rank; }
395  MDSPAN_INLINE_FUNCTION
396  constexpr static rank_type rank_dynamic() noexcept { return m_rank_dynamic; }
397 
398  MDSPAN_INLINE_FUNCTION
399  constexpr index_type extent(rank_type r) const noexcept { return m_vals.value(r); }
400  MDSPAN_INLINE_FUNCTION
401  constexpr static size_t static_extent(rank_type r) noexcept {
402  return vals_t::static_value(r);
403  }
404 
405  // [mdspan.extents.cons], constructors
406  MDSPAN_INLINE_FUNCTION_DEFAULTED
407  constexpr extents() noexcept = default;
408 
409  // Construction from just dynamic or all values.
410  // Precondition check is deferred to maybe_static_array constructor
411  MDSPAN_TEMPLATE_REQUIRES(
412  class... OtherIndexTypes,
413  /* requires */ (
414  _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(is_convertible, OtherIndexTypes,
415  index_type) /* && ... */) &&
416  _MDSPAN_FOLD_AND(_MDSPAN_TRAIT(is_nothrow_constructible, index_type,
417  OtherIndexTypes) /* && ... */) &&
418  (sizeof...(OtherIndexTypes) == m_rank ||
419  sizeof...(OtherIndexTypes) == m_rank_dynamic)))
420  MDSPAN_INLINE_FUNCTION
421  constexpr explicit extents(OtherIndexTypes... dynvals) noexcept
422  : m_vals(static_cast<index_type>(dynvals)...) {}
423 
424  MDSPAN_TEMPLATE_REQUIRES(
425  class OtherIndexType, size_t N,
426  /* requires */
427  (
428  _MDSPAN_TRAIT(is_convertible, OtherIndexType, index_type) &&
429  _MDSPAN_TRAIT(is_nothrow_constructible, index_type,
430  OtherIndexType) &&
431  (N == m_rank || N == m_rank_dynamic)))
432  MDSPAN_INLINE_FUNCTION
433  MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic)
434  constexpr extents(const array<OtherIndexType, N> &exts) noexcept
435  : m_vals(std::move(exts)) {}
436 
437 #ifdef __cpp_lib_span
438  MDSPAN_TEMPLATE_REQUIRES(
439  class OtherIndexType, size_t N,
440  /* requires */
441  (_MDSPAN_TRAIT(is_convertible, OtherIndexType, index_type) &&
442  _MDSPAN_TRAIT(is_nothrow_constructible, index_type, OtherIndexType) &&
443  (N == m_rank || N == m_rank_dynamic)))
444  MDSPAN_INLINE_FUNCTION
445  MDSPAN_CONDITIONAL_EXPLICIT(N != m_rank_dynamic)
446  constexpr extents(const span<OtherIndexType, N> &exts) noexcept
447  : m_vals(std::move(exts)) {}
448 #endif
449 
450 private:
451  // Function to construct extents storage from other extents.
452  // With C++ 17 the first two variants could be collapsed using if constexpr
453  // in which case you don't need all the requires clauses.
454  // in C++ 14 mode that doesn't work due to infinite recursion
455  MDSPAN_TEMPLATE_REQUIRES(
456  size_t DynCount, size_t R, class OtherExtents, class... DynamicValues,
457  /* requires */ ((R < m_rank) && (static_extent(R) == dynamic_extent)))
458  MDSPAN_INLINE_FUNCTION
459  vals_t __construct_vals_from_extents(std::integral_constant<size_t, DynCount>,
460  std::integral_constant<size_t, R>,
461  const OtherExtents &exts,
462  DynamicValues... dynamic_values) noexcept {
463  return __construct_vals_from_extents(
464  std::integral_constant<size_t, DynCount + 1>(),
465  std::integral_constant<size_t, R + 1>(), exts, dynamic_values...,
466  exts.extent(R));
467  }
468 
469  MDSPAN_TEMPLATE_REQUIRES(
470  size_t DynCount, size_t R, class OtherExtents, class... DynamicValues,
471  /* requires */ ((R < m_rank) && (static_extent(R) != dynamic_extent)))
472  MDSPAN_INLINE_FUNCTION
473  vals_t __construct_vals_from_extents(std::integral_constant<size_t, DynCount>,
474  std::integral_constant<size_t, R>,
475  const OtherExtents &exts,
476  DynamicValues... dynamic_values) noexcept {
477  return __construct_vals_from_extents(
478  std::integral_constant<size_t, DynCount>(),
479  std::integral_constant<size_t, R + 1>(), exts, dynamic_values...);
480  }
481 
482  MDSPAN_TEMPLATE_REQUIRES(
483  size_t DynCount, size_t R, class OtherExtents, class... DynamicValues,
484  /* requires */ ((R == m_rank) && (DynCount == m_rank_dynamic)))
485  MDSPAN_INLINE_FUNCTION
486  vals_t __construct_vals_from_extents(std::integral_constant<size_t, DynCount>,
487  std::integral_constant<size_t, R>,
488  const OtherExtents &,
489  DynamicValues... dynamic_values) noexcept {
490  return vals_t{static_cast<index_type>(dynamic_values)...};
491  }
492 
493 public:
494 
495  // Converting constructor from other extents specializations
496  MDSPAN_TEMPLATE_REQUIRES(
497  class OtherIndexType, size_t... OtherExtents,
498  /* requires */
499  (
500  /* multi-stage check to protect from invalid pack expansion when sizes
501  don't match? */
502  decltype(detail::__check_compatible_extents(
503  std::integral_constant<bool, sizeof...(Extents) ==
504  sizeof...(OtherExtents)>{},
505  std::integer_sequence<size_t, Extents...>{},
506  std::integer_sequence<size_t, OtherExtents...>{}))::value))
507  MDSPAN_INLINE_FUNCTION
508  MDSPAN_CONDITIONAL_EXPLICIT((((Extents != dynamic_extent) &&
509  (OtherExtents == dynamic_extent)) ||
510  ...) ||
511  (std::numeric_limits<index_type>::max() <
512  std::numeric_limits<OtherIndexType>::max()))
513  constexpr extents(const extents<OtherIndexType, OtherExtents...> &other) noexcept
514  : m_vals(__construct_vals_from_extents(
515  std::integral_constant<size_t, 0>(),
516  std::integral_constant<size_t, 0>(), other)) {}
517 
518  // Comparison operator
519  template <class OtherIndexType, size_t... OtherExtents>
520  MDSPAN_INLINE_FUNCTION friend constexpr bool
521  operator==(const extents &lhs,
522  const extents<OtherIndexType, OtherExtents...> &rhs) noexcept {
523  bool value = true;
524  for (size_type r = 0; r < m_rank; r++)
525  value &= rhs.extent(r) == lhs.extent(r);
526  return value;
527  }
528 
529 #if !(MDSPAN_HAS_CXX_20)
530  template <class OtherIndexType, size_t... OtherExtents>
531  MDSPAN_INLINE_FUNCTION friend constexpr bool
532  operator!=(extents const &lhs,
533  extents<OtherIndexType, OtherExtents...> const &rhs) noexcept {
534  return !(lhs == rhs);
535  }
536 #endif
537 };
538 
539 // Recursive helper classes to implement dextents alias for extents
540 namespace detail {
541 
542 template <class IndexType, size_t Rank,
545 
546 template <class IndexType, size_t Rank, size_t... ExtentsPack>
548  IndexType, Rank, ::std::experimental::extents<IndexType, ExtentsPack...>> {
549  using type = typename __make_dextents<
550  IndexType, Rank - 1,
552  ::std::experimental::dynamic_extent,
553  ExtentsPack...>>::type;
554 };
555 
556 template <class IndexType, size_t... ExtentsPack>
558  IndexType, 0, ::std::experimental::extents<IndexType, ExtentsPack...>> {
559  using type = ::std::experimental::extents<IndexType, ExtentsPack...>;
560 };
561 
562 } // end namespace detail
563 
564 // [mdspan.extents.dextents], alias template
565 template <class IndexType, size_t Rank>
566 using dextents = typename detail::__make_dextents<IndexType, Rank>::type;
567 
568 // Deduction guide for extents
569 #if defined(_MDSPAN_USE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
570 template <class... IndexTypes>
571 extents(IndexTypes...)
572  -> extents<size_t,
573  size_t((IndexTypes(), ::std::experimental::dynamic_extent))...>;
574 #endif
575 
576 // Helper type traits for identifying a class as extents.
577 namespace detail {
578 
579 template <class T> struct __is_extents : ::std::false_type {};
580 
581 template <class IndexType, size_t... ExtentsPack>
582 struct __is_extents<::std::experimental::extents<IndexType, ExtentsPack...>>
583  : ::std::true_type {};
584 
585 template <class T>
586 #if MDSPAN_HAS_CXX_17
587 inline
588 #else
589 static
590 #endif
591 constexpr bool __is_extents_v = __is_extents<T>::value;
592 
593 } // namespace detail
594 } // namespace experimental
595 } // namespace std
constexpr detail_get::get_impl< i > get
A generalization of std::get, where the index is known at compile time.
Definition: get.hpp:50
Definition: extents.hpp:579
constexpr bool value
T is a fixed or dynamic value that is reducible to a number.
Definition: value.hpp:45
constexpr bool size
T is either an index representing a size, or unbounded_size_t, which indicates that the size is unbou...
Definition: size.hpp:65
Definition: trait_backports.hpp:64
Definition: extents.hpp:110
Definition: extents.hpp:372