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