From dbf468a55895715814102616b682638695de524f Mon Sep 17 00:00:00 2001 From: David Bayer <48736217+davebayer@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:21:33 +0100 Subject: [PATCH] implement C++26 `std::span::at` (#2924) Co-authored-by: Bernhard Manfred Gruber --- .../cuda/std/detail/libcxx/include/span | 19 ++ libcudacxx/include/cuda/std/version | 2 +- .../views/views.span/span.elem/at.pass.cpp | 225 ++++++++++++++++++ 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 libcudacxx/test/libcudacxx/std/containers/views/views.span/span.elem/at.pass.cpp diff --git a/libcudacxx/include/cuda/std/detail/libcxx/include/span b/libcudacxx/include/cuda/std/detail/libcxx/include/span index 8257ac93f1b..afe5ea34519 100644 --- a/libcudacxx/include/cuda/std/detail/libcxx/include/span +++ b/libcudacxx/include/cuda/std/detail/libcxx/include/span @@ -171,6 +171,7 @@ template #include #include #include // for ptrdiff_t +#include // standard-mandated includes #include @@ -502,6 +503,15 @@ public: return __data_[__idx]; } + _LIBCUDACXX_HIDE_FROM_ABI constexpr reference at(size_type __idx) const + { + if (__idx >= size()) + { + _CUDA_VSTD::__throw_out_of_range("span::at"); + } + return __data_[__idx]; + } + _LIBCUDACXX_HIDE_FROM_ABI constexpr reference front() const noexcept { _CCCL_ASSERT(!empty(), "span::front() on empty span"); @@ -731,6 +741,15 @@ public: return __data_[__idx]; } + _LIBCUDACXX_HIDE_FROM_ABI constexpr reference at(size_type __idx) const + { + if (__idx >= size()) + { + _CUDA_VSTD::__throw_out_of_range("span::at"); + } + return __data_[__idx]; + } + _LIBCUDACXX_HIDE_FROM_ABI constexpr reference front() const noexcept { _CCCL_ASSERT(!empty(), "span::front() on empty span"); diff --git a/libcudacxx/include/cuda/std/version b/libcudacxx/include/cuda/std/version index 059bfcccc66..841aa449c77 100644 --- a/libcudacxx/include/cuda/std/version +++ b/libcudacxx/include/cuda/std/version @@ -60,7 +60,7 @@ // # define __cccl_lib_shared_timed_mutex 201402L # endif // !_LIBCUDACXX_HAS_NO_THREADS # define __cccl_lib_source_location 201907L -# define __cccl_lib_span 202002L +# define __cccl_lib_span 202311L // # define __cccl_lib_string_udls 201304L # define __cccl_lib_transformation_trait_aliases 201304L # define __cccl_lib_transparent_operators 201210L diff --git a/libcudacxx/test/libcudacxx/std/containers/views/views.span/span.elem/at.pass.cpp b/libcudacxx/test/libcudacxx/std/containers/views/views.span/span.elem/at.pass.cpp new file mode 100644 index 00000000000..47f45804aad --- /dev/null +++ b/libcudacxx/test/libcudacxx/std/containers/views/views.span/span.elem/at.pass.cpp @@ -0,0 +1,225 @@ +//===----------------------------------------------------------------------===// +// +// Part of the libcu++ Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++11 + +// + +// constexpr reference at(size_type idx) const; + +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +#ifndef TEST_HAS_NO_EXCEPTIONS +# include +#endif // !TEST_HAS_NO_EXCEPTIONS + +template +__host__ __device__ constexpr void testSpanAt(SpanT&& anySpan, int index, int expectedValue) +{ + // non-const + { + auto elem = anySpan.at(index); + ASSERT_SAME_TYPE(ReferenceT, decltype(anySpan.at(index))); + assert(elem == expectedValue); + } + + // const + { + auto elem = cuda::std::as_const(anySpan).at(index); + ASSERT_SAME_TYPE(ReferenceT, decltype(cuda::std::as_const(anySpan).at(index))); + assert(elem == expectedValue); + } +} + +__host__ __device__ constexpr bool test() +{ + // With static extent + { + cuda::std::array arr{0, 1, 2, 3, 4, 5, 9084}; + cuda::std::span arrSpan{arr}; + + assert(cuda::std::dynamic_extent != arrSpan.extent); + + using ReferenceT = typename decltype(arrSpan)::reference; + + testSpanAt(arrSpan, 0, 0); + testSpanAt(arrSpan, 1, 1); + testSpanAt(arrSpan, 6, 9084); + } + + // With dynamic extent + { + cuda::std::array arr{0, 1, 2, 3, 4, 5, 9084}; + cuda::std::span dynSpan{arr}; + + assert(cuda::std::dynamic_extent == dynSpan.extent); + + using ReferenceT = typename decltype(dynSpan)::reference; + + testSpanAt(dynSpan, 0, 0); + testSpanAt(dynSpan, 1, 1); + testSpanAt(dynSpan, 6, 9084); + } + + return true; +} + +#ifndef TEST_HAS_NO_EXCEPTIONS +void test_exceptions() +{ + // With static extent + { + cuda::std::array arr{0, 1, 2, 3, 4, 5, 9084, cuda::std::numeric_limits::max()}; + const cuda::std::span arrSpan{arr}; + + try + { + using SizeT = typename decltype(arrSpan)::size_type; + cuda::std::ignore = arrSpan.at(cuda::std::numeric_limits::max()); + assert(false); + } + catch (const std::out_of_range&) + { + // pass + } + catch (...) + { + assert(false); + } + + try + { + cuda::std::ignore = arrSpan.at(arr.size()); + assert(false); + } + catch (const std::out_of_range&) + { + // pass + } + catch (...) + { + assert(false); + } + + try + { + cuda::std::ignore = arrSpan.at(arr.size() - 1); + // pass + assert(arrSpan.at(arr.size() - 1) == cuda::std::numeric_limits::max()); + } + catch (...) + { + assert(false); + } + } + + { + cuda::std::array arr{}; + const cuda::std::span arrSpan{arr}; + + try + { + cuda::std::ignore = arrSpan.at(0); + assert(false); + } + catch (const std::out_of_range&) + { + // pass + } + catch (...) + { + assert(false); + } + } + + // With dynamic extent + + { + cuda::std::array arr{0, 1, 2, 3, 4, 5, 9084, cuda::std::numeric_limits::max()}; + const cuda::std::span dynSpan{arr}; + + try + { + using SizeT = typename decltype(dynSpan)::size_type; + cuda::std::ignore = dynSpan.at(cuda::std::numeric_limits::max()); + assert(false); + } + catch (const std::out_of_range&) + { + // pass + } + catch (...) + { + assert(false); + } + + try + { + cuda::std::ignore = dynSpan.at(arr.size()); + assert(false); + } + catch (const std::out_of_range&) + { + // pass + } + catch (...) + { + assert(false); + } + + try + { + cuda::std::ignore = dynSpan.at(arr.size() - 1); + assert(dynSpan.at(arr.size() - 1) == cuda::std::numeric_limits::max()); + } + catch (...) + { + assert(false); + } + } + + { + cuda::std::array arr{}; + const cuda::std::span dynSpan{arr}; + + try + { + cuda::std::ignore = dynSpan.at(0); + assert(false); + } + catch (const std::out_of_range&) + { + // pass + } + catch (...) + { + assert(false); + } + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +int main(int, char**) +{ + test(); + static_assert(test(), ""); + +#ifndef TEST_HAS_NO_EXCEPTIONS + NV_IF_TARGET(NV_IS_HOST, (test_exceptions();)) +#endif // TEST_HAS_NO_EXCEPTIONS + + return 0; +}