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