1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //                        Kokkos v. 4.0
9 //       Copyright (2022) National Technology & Engineering
10 //               Solutions of Sandia, LLC (NTESS).
11 //
12 // Under the terms of Contract DE-NA0003525 with NTESS,
13 // the U.S. Government retains certain rights in this software.
14 //
15 //===---------------------------------------------------------------------===//
16 
17 #ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H
18 #define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H
19 
20 #include <mdspan>
21 #include <type_traits>
22 #include <cassert>
23 
24 // This contains a bunch of accessors and handles which have different properties
25 // regarding constructibility and convertibility in order to test mdspan constraints
26 
27 // non default constructible data handle
28 template <class T>
29 struct no_default_ctor_handle {
30   T* ptr;
31   no_default_ctor_handle() = delete;
no_default_ctor_handleno_default_ctor_handle32   constexpr no_default_ctor_handle(T* ptr_) : ptr(ptr_) {}
33 };
34 
35 // handle that can't convert from T to const T
36 template <class T>
37 struct not_const_convertible_handle {
38   T* ptr;
not_const_convertible_handlenot_const_convertible_handle39   constexpr not_const_convertible_handle() : ptr(nullptr) {}
not_const_convertible_handlenot_const_convertible_handle40   constexpr not_const_convertible_handle(T* ptr_) : ptr(ptr_) {}
41 
42   constexpr T& operator[](size_t i) const { return ptr[i]; }
43 };
44 
45 // handle where move has side effects
46 template <class T>
47 struct move_counted_handle {
48   T* ptr;
49   constexpr move_counted_handle()                           = default;
50   constexpr move_counted_handle(const move_counted_handle&) = default;
51   template <class OtherT>
requiresmove_counted_handle52     requires(std::is_constructible_v<T*, OtherT*>)
53   constexpr move_counted_handle(const move_counted_handle<OtherT>& other) : ptr(other.ptr) {}
move_counted_handlemove_counted_handle54   constexpr move_counted_handle(move_counted_handle&& other) {
55     ptr = other.ptr;
56     if (!std::is_constant_evaluated()) {
57       move_counter()++;
58     }
59   }
move_counted_handlemove_counted_handle60   constexpr move_counted_handle(T* ptr_) : ptr(ptr_) {}
61 
62   constexpr move_counted_handle& operator=(const move_counted_handle&) = default;
63 
64   constexpr T& operator[](size_t i) const { return ptr[i]; }
65 
move_countermove_counted_handle66   static int& move_counter() {
67     static int c = 0;
68     return c;
69   }
70 };
71 
72 // non-default constructible accessor with a bunch of different data handles
73 template <class ElementType>
74 struct checked_accessor {
75   size_t N;
76   using offset_policy    = std::default_accessor<ElementType>;
77   using element_type     = ElementType;
78   using reference        = ElementType&;
79   using data_handle_type = move_counted_handle<ElementType>;
80 
checked_accessorchecked_accessor81   constexpr checked_accessor(size_t N_) : N(N_) {}
82   template <class OtherElementType>
requireschecked_accessor83     requires(std::is_convertible_v<OtherElementType (*)[], element_type (*)[]>)
84   explicit constexpr checked_accessor(const checked_accessor<OtherElementType>& other) noexcept {
85     N = other.N;
86   }
87 
accesschecked_accessor88   constexpr reference access(data_handle_type p, size_t i) const noexcept {
89     assert(i < N);
90     return p[i];
91   }
offsetchecked_accessor92   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
93     assert(i < N);
94     return data_handle_type(p.ptr + i);
95   }
96 };
97 
98 static_assert(std::is_constructible_v<checked_accessor<const int>, const checked_accessor<int>&>);
99 static_assert(!std::is_convertible_v<const checked_accessor<int>&, checked_accessor<const int>>);
100 
101 template <>
102 struct checked_accessor<double> {
103   size_t N;
104   using offset_policy    = std::default_accessor<double>;
105   using element_type     = double;
106   using reference        = double&;
107   using data_handle_type = no_default_ctor_handle<double>;
108 
109   constexpr checked_accessor(size_t N_) : N(N_) {}
110 
111   template <class OtherElementType>
112     requires(std::is_convertible_v<OtherElementType (*)[], element_type (*)[]>)
113   constexpr checked_accessor(checked_accessor<OtherElementType>&& other) noexcept {
114     N = other.N;
115   }
116 
117   constexpr reference access(data_handle_type p, size_t i) const noexcept {
118     assert(i < N);
119     return p.ptr[i];
120   }
121   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
122     assert(i < N);
123     return p.ptr + i;
124   }
125 };
126 
127 template <>
128 struct checked_accessor<unsigned> {
129   size_t N;
130   using offset_policy    = std::default_accessor<unsigned>;
131   using element_type     = unsigned;
132   using reference        = unsigned;
133   using data_handle_type = not_const_convertible_handle<unsigned>;
134 
135   constexpr checked_accessor() : N(0) {}
136   constexpr checked_accessor(size_t N_) : N(N_) {}
137   constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
138 
139   constexpr reference access(data_handle_type p, size_t i) const noexcept {
140     assert(i < N);
141     return p[i];
142   }
143   constexpr auto offset(data_handle_type p, size_t i) const noexcept {
144     assert(i < N);
145     return p.ptr + i;
146   }
147 };
148 template <>
149 struct checked_accessor<const unsigned> {
150   size_t N;
151   using offset_policy    = std::default_accessor<const unsigned>;
152   using element_type     = const unsigned;
153   using reference        = unsigned;
154   using data_handle_type = not_const_convertible_handle<const unsigned>;
155 
156   constexpr checked_accessor() : N(0) {}
157   constexpr checked_accessor(size_t N_) : N(N_) {}
158   constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
159 
160   template <class OtherACC>
161   constexpr explicit(std::is_const_v<OtherACC>) checked_accessor(OtherACC&& acc) : N(acc.N) {}
162 
163   constexpr reference access(data_handle_type p, size_t i) const noexcept {
164     assert(i < N);
165     return p[i];
166   }
167   constexpr auto offset(data_handle_type p, size_t i) const noexcept {
168     assert(i < N);
169     return p.ptr + i;
170   }
171 };
172 
173 template <>
174 struct checked_accessor<const float> {
175   size_t N;
176   using offset_policy    = std::default_accessor<const float>;
177   using element_type     = const float;
178   using reference        = const float&;
179   using data_handle_type = move_counted_handle<const float>;
180 
181   constexpr checked_accessor() : N(0) {}
182   constexpr checked_accessor(size_t N_) : N(N_) {}
183   constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
184 
185   constexpr checked_accessor(checked_accessor<float>&& acc) : N(acc.N) {}
186 
187   constexpr reference access(data_handle_type p, size_t i) const noexcept {
188     assert(i < N);
189     return p[i];
190   }
191   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
192     assert(i < N);
193     return data_handle_type(p.ptr + i);
194   }
195 };
196 
197 template <>
198 struct checked_accessor<const double> {
199   size_t N;
200   using offset_policy    = std::default_accessor<const double>;
201   using element_type     = const double;
202   using reference        = const double&;
203   using data_handle_type = move_counted_handle<const double>;
204 
205   constexpr checked_accessor() : N(0) {}
206   constexpr checked_accessor(size_t N_) : N(N_) {}
207   constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
208 
209   constexpr reference access(data_handle_type p, size_t i) const noexcept {
210     assert(i < N);
211     return p[i];
212   }
213   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
214     assert(i < N);
215     return data_handle_type(p.ptr + i);
216   }
217 };
218 
219 // Data handle pair which has configurable conversion properties
220 // bool template parameters are used to enable/disable ctors and assignment
221 // the c is the one for const T the nc for non-const (so we can convert mdspan)
222 // Note both take non-const T as template parameter though
223 template <class T, bool, bool, bool, bool>
224 struct conv_test_accessor_c;
225 
226 template <class T, bool conv_c, bool conv_nc>
227 struct conv_test_accessor_nc {
228   using offset_policy    = std::default_accessor<T>;
229   using element_type     = T;
230   using reference        = T&;
231   using data_handle_type = T*;
232 
233   constexpr conv_test_accessor_nc()                             = default;
234   constexpr conv_test_accessor_nc(const conv_test_accessor_nc&) = default;
235 
236   template <bool b1, bool b2, bool b3, bool b4>
237   constexpr operator conv_test_accessor_c<T, b1, b2, b3, b4>()
238     requires(conv_nc)
239   {
240     return conv_test_accessor_c<T, b1, b2, b3, b4>{};
241   }
242   template <bool b1, bool b2, bool b3, bool b4>
243   constexpr operator conv_test_accessor_c<T, b1, b2, b3, b4>() const
244     requires(conv_c)
245   {
246     return conv_test_accessor_c<T, b1, b2, b3, b4>{};
247   }
248 
249   constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; }
250   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; }
251 };
252 
253 template <class T, bool ctor_c, bool ctor_mv, bool assign_c, bool assign_mv>
254 struct conv_test_accessor_c {
255   using offset_policy    = std::default_accessor<const T>;
256   using element_type     = const T;
257   using reference        = const T&;
258   using data_handle_type = const T*;
259 
260   constexpr conv_test_accessor_c()                            = default;
261   constexpr conv_test_accessor_c(const conv_test_accessor_c&) = default;
262 
263   template <bool b1, bool b2>
264   constexpr conv_test_accessor_c(const conv_test_accessor_nc<T, b1, b2>&)
265     requires(ctor_c)
266   {}
267   template <bool b1, bool b2>
268   constexpr conv_test_accessor_c(conv_test_accessor_nc<T, b1, b2>&&)
269     requires(ctor_mv)
270   {}
271   template <bool b1, bool b2>
272   constexpr conv_test_accessor_c& operator=(const conv_test_accessor_nc<T, b1, b2>&)
273     requires(assign_c)
274   {
275     return {};
276   }
277   template <bool b1, bool b2>
278   constexpr conv_test_accessor_c& operator=(conv_test_accessor_nc<T, b1, b2>&&)
279     requires(assign_mv)
280   {
281     return {};
282   }
283 
284   constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; }
285   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; }
286 };
287 
288 template <class ElementType>
289 struct convertible_accessor_but_not_handle {
290   size_t N;
291   using offset_policy    = std::default_accessor<ElementType>;
292   using element_type     = ElementType;
293   using reference        = ElementType&;
294   using data_handle_type = not_const_convertible_handle<element_type>;
295 
296   constexpr convertible_accessor_but_not_handle() = default;
297   template <class OtherElementType>
298     requires(std::is_convertible_v<OtherElementType (*)[], element_type (*)[]>)
299   explicit constexpr convertible_accessor_but_not_handle(
300       const convertible_accessor_but_not_handle<OtherElementType>& other) noexcept {
301     N = other.N;
302   }
303 
304   constexpr reference access(data_handle_type p, size_t i) const noexcept {
305     assert(i < N);
306     return p[i];
307   }
308   constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
309     assert(i < N);
310     return data_handle_type(p.ptr + i);
311   }
312 };
313 
314 #endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H
315