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