cuda-kat
CUDA kernel author's tools
span.hpp
Go to the documentation of this file.
1 
10 // Copyright (C) 2019-2020 Free Software Foundation, Inc.
11 // Copyright (C) 2020 Eyal Rozenberg <eyalroz@technion.ac.il>
12 //
13 // This file is based on <span> from the GNU ISO C++ Library. It is
14 // free software. Thus, you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the
16 // Free Software Foundation; either version 3, or (at your option)
17 // any later version.
18 //
19 // Under Section 7 of GPL version 3, you are granted additional
20 // permissions described in the GCC Runtime Library Exception, version
21 // 3.1, as published by the Free Software Foundation, i.e.:
22 //
23 // You may use this file as part of a free software library without
24 // restriction. Specifically, if other files instantiate templates or
25 // use macros or inline functions from this file, or you compile this file
26 // and link it with other files to produce an executable, this file does
27 // not by itself cause the resulting executable to be covered by the GNU
28 // General Public License. This exception does not however invalidate any
29 // other reasons why the executable file might be covered by the GNU
30 // General Public License.
31 //
32 // A copy of the GNU General Public License and a copy of the GCC Runtime
33 // Library Exception are available at <http://www.gnu.org/licenses/>.
34 
35 #ifndef KAT_CONTAINERS_SPAN_HPP_
36 #define KAT_CONTAINERS_SPAN_HPP_
37 
38 #include <type_traits>
39 #include <iterator>
40 #include <array>
41 #include <cassert>
42 #include <kat/containers/array.hpp>
43 #include "detail/normal_iterator.hpp"
44 #include <kat/detail/execution_space_specifiers.hpp>
45 #include <kat/detail/range_access.hpp>
46 
47 
48 #if __cplusplus >= 202001L
49 // #include <bits/range_access.h> <- TODO: What replaces this?
50 #endif
51 
52 namespace kat {
53 
54 #if __cplusplus >= 201703L
55 inline
56 #endif
57 constexpr const std::size_t dynamic_extent = static_cast<std::size_t>(-1);
58 
59 template<typename Type, std::size_t Extent>
60 class span;
61 
62 namespace detail {
63 
64 template <typename T>
65 #if __cplusplus >= 202001L
66 using iter_reference_t = std::iter_reference_t<T>;
67 #else
68 using iter_reference_t = decltype(*std::declval<T&>());
69 #endif
70 
71 template<typename T>
72 struct is_span : std::false_type {};
73 
74 template<typename T, std::size_t Num>
75 struct is_span<kat::span<T, Num>> : std::true_type {};
76 
77 #if __cplusplus >= 202001L
78 template<typename T, std::size_t Num>
79 struct is_span<std::span<T, Num>> : std::true_type {};
80 
81 template<typename T, std::size_t Num>
82 struct is_span<std::span<T, Num>> : std::true_type {};
83 #endif
84 // TODO: Entries here for gsl::span
85 
86 /*
87 
88  template<typename T>
89  struct __is_std_array : std::false_type { };
90 
91 
92  template<typename T, std::size_t Num>
93  struct __is_std_array<_GLIBCXX_STD_C::array<T, Num>> : std::true_type { };
94 */
95 
96 template<std::size_t Extent>
98 public:
99  constexpr KAT_HD extent_storage(std::size_t) noexcept
100  {
101  }
102 
103  static constexpr KAT_HD std::size_t extent() noexcept
104  {
105  return Extent;
106  }
107 };
108 
109 template<>
110 class extent_storage<dynamic_extent> {
111 public:
112  constexpr KAT_HD extent_storage(std::size_t extent) noexcept
113  : extent_value(extent)
114  {}
115 
116  constexpr KAT_HD std::size_t
117  extent() const noexcept
118  {
119  return this->extent_value;
120  }
121 
122 private:
123  std::size_t extent_value;
124 };
125 
126 } // namespace detail
127 
128 template<typename Type, std::size_t Extent = dynamic_extent>
129 class span {
130 
131 public:
132  // TODO: These two methods should be private (and are, in the libstdc++ sources.
133  // Unfortunately it gives me trouble when accessing them during construction of a length-0 span in some cases
134  template<std::size_t Offset, std::size_t Count, typename = typename std::enable_if<Count != dynamic_extent>::type>
135  static constexpr KAT_HD std::size_t S_subspan_extent()
136  {
137  static_assert(Count != dynamic_extent, "This implementation should not have been instantiated for Count == dynamic_extent...");
138  return Count;
139  }
140 
141  template<std::size_t Offset>
142  static constexpr KAT_HD std::size_t S_subspan_extent<Offset, dynamic_extent>()
143  {
144  #if __cplusplus >= 201703L
145  if constexpr KAT_HD (extent != dynamic_extent)
146  #else
147  if (extent != dynamic_extent)
148  #endif
149  return Extent - Offset;
150  else
151  return dynamic_extent;
152 
153  }
154 
155 private:
156  // TODO: Where does __is_array_convertible come from?? I can't use it for now,
157  // so the criteria here become trivial before C++2020
158 
159  // _GLIBCXX_RESOLVE_LIB_DEFECTS
160  // 3255. span's array constructor is too strict
161  template<typename T, std::size_t ArrayExtent>
162  using is_compatible_array = std::integral_constant<bool,
163  true
164  and (Extent == dynamic_extent or ArrayExtent == Extent)
165  and (std::is_const<Type>::value or not std::is_const<T>::value)
166 #if false
167  and __is_array_convertible<Type, T>
168 #endif
169  >;
170 
171  template<typename Iter, typename Ref = detail::iter_reference_t<Iter>>
172  using is_compatible_iterator = std::integral_constant<bool,
173  true
174 #if __cplusplus >= 202001L
175 #if false
176  and contiguous_iterator<Iter>::value // where does this come from?
177 #endif
178  and std::is_lvalue_reference<detail::iter_reference_t<Iter>>::value,
179  and std::is_same<std::iter_value_t<Iter>, std::remove_cvref_t<Ref>>::value,
180 #endif
181 #if false
182  and __is_array_convertible<Type, std::remove_reference_t<Ref>>::value
183 #endif
184  >;
185 
186 #if __cplusplus >= 202001L
187  template<typename Range>
188  using __is_compatible_range
189  = is_compatible_iterator<ranges::iterator_t<Range>>;
190 #endif
191 
192 public:
193  // member types
194  using value_type = typename std::remove_cv<Type>::type;
195  using element_type = Type;
196  using size_type = std::size_t;
197  using reference = element_type&;
198  using const_reference = const element_type&;
199  using pointer = Type*;
200  using const_pointer = const Type*;
201 
202  using iterator = kat::detail::normal_iterator<pointer, span>;
203  using const_iterator = kat::detail::normal_iterator<const_pointer, span>;
204  using reverse_iterator = std::reverse_iterator<iterator>;
205  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
206  using difference_type = std::ptrdiff_t;
207 
208  // member constants
209 #if __cplusplus >= 201703L
210  inline
211 #endif
212  static constexpr const std::size_t extent = Extent;
213 
214  // constructors
215 
216  constexpr KAT_HD span() noexcept
217 #if __cplusplus >= 202001L
218  requires ((Extent + 1u) <= 1u)
219 #endif
220  : extent_(0), ptr_(nullptr)
221  {
222 #if __cplusplus < 202001L
223  static_assert((Extent + 1u) <= 1u, "Invalid Extent value - for this constructor, can be either dynamic_extent or 0");
224 #endif
225  }
226 
227  constexpr KAT_HD
228  span(const span&) noexcept = default;
229 
230 #if __cplusplus >= 202001L
231  template<typename T, std::size_t ArrayExtent>
232  requires (is_compatible_array<T, ArrayExtent>::value)
233 #else
234  template<typename T, std::size_t ArrayExtent, typename = typename std::enable_if<is_compatible_array<T, ArrayExtent>::value>::type >
235 #endif
236  constexpr KAT_HD
237  span(T (&arr)[ArrayExtent]) noexcept
238  : span(static_cast<pointer>(arr), ArrayExtent)
239  {
240 // here
241  static_assert(is_compatible_array<T, ArrayExtent>::value, "Attempt to construct a span with an incompatible raw array");
242  }
243 
244 #if __cplusplus >= 202001L
245  template<typename T, std::size_t ArrayExtent>
246  requires (is_compatible_array<T, ArrayExtent>::value)
247 #else
248  template<typename T, std::size_t ArrayExtent, typename = typename std::enable_if<is_compatible_array<T, ArrayExtent>::value>::type >
249 #endif
250  constexpr KAT_HD
251  span(kat::array<T, ArrayExtent>& arr) noexcept
252  : span(static_cast<pointer>(arr.data()), ArrayExtent)
253  {
254  static_assert(is_compatible_array<T, ArrayExtent>::value, "Attempt to construct a span with an incompatible kat::array");
255  }
256 
257 #if __cplusplus >= 202001L
258  template<typename T, std::size_t ArrayExtent>
259  requires (is_compatible_array<T, ArrayExtent>::value)
260 #else
261  template<typename T, std::size_t ArrayExtent, typename = typename std::enable_if<is_compatible_array<const T, ArrayExtent>::value>::type >
262 #endif
263  constexpr KAT_HD span(const kat::array<T, ArrayExtent>& arr) noexcept
264  : span(static_cast<pointer>(arr.data()), ArrayExtent)
265  {
266  static_assert(is_compatible_array<T, ArrayExtent>::value, "Attempt to construct a span with an incompatible const kat::array");
267  }
268 
269 #if __cplusplus >= 202001L
270  template<typename T, std::size_t ArrayExtent>
271  requires (is_compatible_array<T, ArrayExtent>::value)
272 #else
273  template<typename T, std::size_t ArrayExtent, typename = typename std::enable_if<is_compatible_array<T, ArrayExtent>::value>::type >
274 #endif
275  constexpr KAT_HOST span(std::array<T, ArrayExtent>& arr) noexcept
276  : span(static_cast<pointer>(arr.data()), ArrayExtent)
277  {
278  static_assert(is_compatible_array<T, ArrayExtent>::value, "Constructing a span with an incompatible std::array");
279  }
280 
281 #if __cplusplus >= 202001L
282  template<typename T, std::size_t ArrayExtent>
283  requires (is_compatible_array<T, ArrayExtent>::value)
284 #else
285  template<typename T, std::size_t ArrayExtent, typename = typename std::enable_if<is_compatible_array<const T, ArrayExtent>::value>::type >
286 #endif
287  constexpr KAT_HOST span(const std::array<T, ArrayExtent>& arr) noexcept
288  : span(static_cast<pointer>(arr.data()), ArrayExtent)
289  {
290  static_assert(is_compatible_array<T, ArrayExtent>::value, "Constructing a span with an incompatible const std::array");
291  }
292 
293 
294 
295 public:
296  // Note: Currently, we can't construct spans from Containers (except through ranges in C++20)
297 
298 #if __cplusplus >= 202001L
299  template<ranges::contiguous_range Range>
300  requires (Extent == dynamic_extent)
301  and (!detail::is_span<std::remove_cvref_t<Range>>::value)
302  and (!detail::__is_std_array<std::remove_cvref_t<Range>>::value)
303  and (!is_array_v<std::remove_reference_t<Range>>)
304  and (__is_compatible_range<Range>::value)
305  constexpr KAT_HD
306  span(Range&& range)
307  noexcept(noexcept(ranges::data(range))
308  and noexcept(ranges::size(range)))
309  : span(ranges::data(range), ranges::size(range))
310  { }
311 #endif
312 
313 
314 
315 #if __cplusplus >= 202001L
316  template<contiguous_iterator ContiguousIterator,
317  sized_sentinel_for<ContiguousIterator> Sentinel>
318  requires (is_compatible_iterator<ContiguousIterator>::value)
319  and (!is_convertible_v<Sentinel, size_type>)
320  constexpr KAT_HD
321  span(ContiguousIterator first, Sentinel last)
322  noexcept(noexcept(last - first))
323  : extent_(static_cast<size_type>(last - first)),
324  ptr_(std::to_address(first))
325  {
326  if (Extent != dynamic_extent)
327  assert((last - first) == Extent);
328  }
329 #else
330  constexpr KAT_HD span(pointer first, pointer last) noexcept
331  : extent_(static_cast<size_type>(last - first)),
332  ptr_(first)
333  { }
334 #endif
335 
336 #if __cplusplus >= 202001L
337  template<contiguous_iterator ContiguousIterator>
338  requires (is_compatible_iterator<ContiguousIterator>::value)
339  constexpr KAT_HD
340  span(ContiguousIterator first, size_type count)
341  noexcept
342  : extent_(count), ptr_(std::to_address(first))
343  { assert(Extent == dynamic_extent or count == Extent); }
344 #else
345  constexpr KAT_HD span(pointer first, std::size_t count) noexcept
346  : extent_(count), ptr_(first)
347  { }
348 #endif
349 
350 
351  template<typename OType, std::size_t OExtent>
352 #if __cplusplus >= 2017031L
353  requires (Extent == dynamic_extent or Extent == OExtent)
354  and (__is_array_convertible<Type, OType>::value)
355 #endif
356  constexpr KAT_HD
357  span(const span<OType, OExtent>& s) noexcept
358  : extent_(s.size()), ptr_(s.data())
359  {
360 #if __cplusplus < 2017031L
361  static_assert(Extent == dynamic_extent or Extent == OExtent, "Invalid extent");
362  // what about is_array_convertible? :-(
363 #endif
364  }
365 
366  // assignment
367 
368  constexpr KAT_HD span&
369  operator=(const span&) noexcept = default;
370 
371  // observers
372 
373  constexpr KAT_HD size_type
374  size() const noexcept
375  { return extent_.extent();}
376 
377  constexpr KAT_HD size_type
378  size_bytes() const noexcept
379  { return extent_.extent() * sizeof(element_type);}
380 
381  [[nodiscard]] constexpr KAT_HD bool
382  empty() const noexcept
383  { return size() == 0;}
384 
385  // element access
386 
387  constexpr KAT_HD reference
388  front() const noexcept
389  {
390  static_assert(extent != 0, "Zero span extent");
391  assert(!empty());
392  return *ptr_;
393  }
394 
395  constexpr KAT_HD reference
396  back() const noexcept
397  {
398  static_assert(extent != 0, "Zero span extent");
399  assert(!empty());
400  return *(ptr_ + (size() - 1));
401  }
402 
403  constexpr KAT_HD reference
404  operator[](size_type idx) const noexcept
405  {
406  static_assert(extent != 0, "Zero span extent");
407  assert(idx < size());
408  return *(ptr_ + idx);
409  }
410 
411  constexpr KAT_HD pointer
412  data() const noexcept
413  { return ptr_;}
414 
415  // iterator support
416 
417  constexpr KAT_HD iterator
418  begin() const noexcept
419  { return iterator(ptr_);}
420 
421  constexpr KAT_HD const_iterator
422  cbegin() const noexcept
423  { return const_iterator(ptr_);}
424 
425  constexpr KAT_HD iterator
426  end() const noexcept
427  { return iterator(ptr_ + size());}
428 
429  constexpr KAT_HD const_iterator
430  cend() const noexcept
431  { return const_iterator(ptr_ + size());}
432 
433  constexpr KAT_HD reverse_iterator
434  rbegin() const noexcept
435  { return reverse_iterator(this->end());}
436 
437  constexpr KAT_HD const_reverse_iterator
438  crbegin() const noexcept
439  { return const_reverse_iterator(this->cend());}
440 
441  constexpr KAT_HD reverse_iterator
442  rend() const noexcept
443  { return reverse_iterator(this->begin());}
444 
445  constexpr KAT_HD const_reverse_iterator
446  crend() const noexcept
447  { return const_reverse_iterator(this->cbegin());}
448 
449  // subviews
450 
451  template<std::size_t Count>
452  constexpr KAT_HD span<element_type, Count>
453  first() const noexcept
454  {
455  #if __cplusplus >= 201703L
456  if constexpr KAT_HD (Extent == dynamic_extent)
457  assert(Count <= size());
458  else
459  static_assert(Count <= extent);
460  #else
461  if (Extent == dynamic_extent) {
462  assert ((Count < size() or Count == size()) and "Span fixed element count exceeds its size");
463  }
464  else {
465  assert ((size() < Extent or size() == Extent ) and "Span size exceeds its extent");
466  }
467  #endif
468  return { data(), Count };
469  }
470 
471  constexpr KAT_HD span<element_type, dynamic_extent>
472  first(size_type count) const noexcept
473  {
474  assert(count <= size());
475  return { data(), count };
476  }
477 
478  template<std::size_t Count>
479  constexpr KAT_HD span<element_type, Count>
480  last() const noexcept
481  {
482  #if __cplusplus >= 201703L
483  if constexpr KAT_HD (Extent == dynamic_extent)
484  assert(Count <= size());
485  else
486  static_assert(Count <= extent);
487  #else
488  if (Extent == dynamic_extent) {
489  assert ((Count < size() or Count == size()) and "Span fixed element count exceeds its size");
490  }
491  else {
492  assert ((size() < Extent or size() == Extent) and "Span size exceeds its extent");
493  }
494  #endif
495  return {data() + (size() - Count), Count};
496  }
497 
498  constexpr KAT_HD span<element_type, dynamic_extent>
499  last(size_type count) const noexcept
500  {
501  assert(count <= size());
502  return {data() + (size() - count), count};
503  }
504 
505  template<std::size_t Offset, std::size_t Count = dynamic_extent>
506  constexpr KAT_HD auto
507  subspan() const noexcept
509  {
510  #if __cplusplus >= 201703L
511  if constexpr KAT_HD (Extent == dynamic_extent)
512  assert(Offset <= size());
513  else
514  static_assert(Offset <= extent);
515  #else
516  if (Extent == dynamic_extent) {
517  assert((Offset < size() or Offset == size()) and "Subspan offset exceeds span size");
518  }
519  else{
520  assert((Offset < Extent or Offset == Extent) and "Subspan offset exceeds fixed span extent");
521  }
522  #endif
523 
524  #if __cplusplus >= 201703L
525  if constexpr KAT_HD (Extent == dynamic_extent)
526  {
527  assert(Count <= size());
528  assert(Count <= (size() - Offset));
529  }
530  else
531  {
532  static_assert(Count <= extent);
533  static_assert(Count <= (extent - Offset));
534  }
535  #else
536  if (Extent == dynamic_extent) {
537  // Note: not using <= comparisons in the assertions to avoid compiler warnings
538  assert((size() > Count or size() == Count ) and "Count exceeds span size");
539  assert( (Count < (size() - Offset) or Count == (size() - Offset)) and "Subspan ends past the span's end");
540  }
541  else {
542  assert((Count <= extent) and "Count exceeds span extent");
543  assert(Count <= (extent - Offset) and "Subspan ends past the span's extent");
544  }
545  #endif
546 
547  return {data() + Offset, Count};
548  }
549 
550  template<std::size_t Offset>
551  constexpr KAT_HD auto
552  subspan<Offset, dynamic_extent>() const noexcept
553  -> span<element_type, S_subspan_extent<Offset, dynamic_extent>()>
554  {
555  #if __cplusplus >= 201703L
556  if constexpr KAT_HD (Extent == dynamic_extent)
557  assert(Offset <= size());
558  else
559  static_assert(Offset <= extent);
560  #else
561  if (Extent == dynamic_extent) {
562  assert(Offset <= size() and "Subspan offset exceeds span size");
563  }
564  else {
565  assert((Offset <= Extent) and "Subspan offset exceeds fixed span extent");
566  }
567  #endif
568  return {data() + Offset, size() - Offset};
569  }
570 
571  constexpr KAT_HD span<element_type, dynamic_extent>
572  subspan(size_type offset, size_type count = dynamic_extent) const
573  noexcept
574  {
575  assert(offset <= size());
576  if (count == dynamic_extent) {
577  count = size() - offset;
578  }
579  else
580  {
581  assert(count <= size());
582  assert(offset + count <= size());
583  }
584  return {data() + offset, count};
585  }
586 
587 private:
588 #if __cplusplus >= 202001L
589  [[no_unique_address]]
590 #endif
592  pointer ptr_;
593 }; // class span
594 
595 #if __cplusplus < 201703L
596 template<typename Type, std::size_t Extent>
597 constexpr const std::size_t span<Type, Extent>::extent;
598 #endif
599 
600 
601 #if __cplusplus >= 201703L
602  // deduction guides
603  template<typename Type, std::size_t ArrayExtent>
604  span(Type(&)[ArrayExtent]) -> span<Type, ArrayExtent>;
605 
606  template<typename Type, std::size_t ArrayExtent>
608 
609  template<typename Type, std::size_t ArrayExtent>
610  span(std::array<Type, ArrayExtent>&) -> span<Type, ArrayExtent>;
611 
612  template<typename Type, std::size_t ArrayExtent>
615 
616  template<typename Type, std::size_t ArrayExtent>
617  span(const std::array<Type, ArrayExtent>&)
619 
620  template<contiguous_iterator Iter, typename Sentinel>
621  span(Iter, Sentinel)
623 
624  template<typename Range>
625  span(Range &&)
627 #endif
628 
629 #if __cplusplus >= 201703L
630 template<typename Type, std::size_t Extent>
632  span<Type, Extent> sp) noexcept
633 {
634  return {reinterpret_cast<const byte*>(sp.data()), sp.size_bytes()};
635 }
636 
637 template<typename Type, std::size_t Extent>
639  span<Type, Extent> sp) noexcept
640 {
641  return {reinterpret_cast<byte*>(sp.data()), sp.size_bytes()};
642 }
643 #endif
644 
645 // The following code is useful for pre-C++20 code, where
646 // the span CTAD is unavailable
647 
648 // TODO: Fixed-extent make_span's
649 
650 template<class Type>
651 constexpr KAT_HD
652 span<Type> make_span(Type* first, Type* last)
653 {
654  return span<Type>(first, last);
655 }
656 
657 template<class Type>
658 constexpr KAT_HD
659 span<Type> make_span(Type* ptr, std::size_t count)
660 {
661  return span<Type>(ptr, count);
662 }
663 
664 template<class Type, std::size_t Extent>
665 constexpr KAT_HD
666 span<Type, Extent> make_span(Type (&arr)[Extent])
667 {
668  return span<Type, Extent>(arr);
669 }
670 
671 template<class Type, std::size_t Extent>
672 constexpr KAT_HD
674 make_span(const kat::array<Type, Extent>& arr)
675 {
676  return span<const Type, Extent>(arr);
677 }
678 
679 template<class Type, std::size_t Extent>
680 constexpr KAT_HD
682 make_span(kat::array<Type, Extent>& arr)
683 {
684  return span<Type, Extent>(arr);
685 }
686 
687 template<class Container>
688 constexpr KAT_HD
690 make_span(Container &container)
691 {
692  return span<typename Container::value_type>(container);
693 }
694 
695 template<class Container>
696 constexpr KAT_HD
698 make_span(const Container &container)
699 {
701 }
702 
703 template<class Pointer>
704 constexpr KAT_HD
706 make_span(Pointer& container, std::size_t count)
707 {
708  return span<typename Pointer::element_type>(container, count);
709 }
710 
711 template<class Pointer>
712 constexpr KAT_HD
714 make_span(Pointer& container)
715 {
716  return span<typename Pointer::element_type>(container);
717 }
718 
719 } // namespace kat
720 
721 namespace std {
722 
723 // tuple helpers
724 template<std::size_t Index, typename Type, std::size_t Extent>
725 constexpr KAT_HD Type&
726 get(kat::span<Type, Extent> sp) noexcept
727 {
728  static_assert(Extent != kat::dynamic_extent and Index < Extent,
729  "get<I> can only be used with a span of non-dynamic (fixed) extent");
730  return sp[Index];
731 }
732 
733 template<typename T> struct tuple_size;
734 template<std::size_t i, typename T> struct tuple_element;
735 
736 template<typename Type, std::size_t Extent>
737 struct tuple_size<kat::span<Type, Extent>> : public std::integral_constant<std::size_t, Extent> {
738  static_assert(Extent != kat::dynamic_extent, "tuple_size can only "
739  "be used with a span of non-dynamic (fixed) extent");
740 };
741 
742 template<std::size_t Index, typename Type, std::size_t Extent>
743 struct tuple_element<Index, kat::span<Type, Extent>> {
744  static_assert(Extent != kat::dynamic_extent, "tuple_element can only "
745  "be used with a span of non-dynamic (fixed) extent");
746  static_assert(Index < Extent, "Index is less than Extent");
747  using type = Type;
748 };
749 
750 #if __cplusplus >= 202001L
751 namespace ranges
752 {
753  template<typename> extern inline const bool enable_safe_range;
754  // Opt-in to safe_range concept
755  template<typename _ElementType, std::size_t Extent>
756  inline constexpr KAT_HD bool
757  enable_safe_range<kat::span<_ElementType, Extent>> = true;
758 }
759 #endif
760 
761 } // namespace std
762 
763 #endif // KAT_CONTAINERS_SPAN_HPP_
Definition: tuple.hpp:60
Definition: array.hpp:353
Definition: span.hpp:97
STL namespace.
Definition: common.hpp:16
Definition: span.hpp:72
Definition: tuple.hpp:63
This file implements kat::array, an equivalent of C++11&#39;s std::array which may be used both in host-s...
Definition: array.hpp:358
Definition: span.hpp:60
A standard container for storing a fixed size sequence of elements, based on std::array - but fully G...
Definition: array.hpp:81