Skip to content

Commit

Permalink
implement C++26 std::span's constructor from std::initializer_list (
Browse files Browse the repository at this point in the history
#2923)

Co-authored-by: Michael Schellenberger Costa <[email protected]>
  • Loading branch information
davebayer and miscco authored Nov 22, 2024
1 parent b27d512 commit 4ae70bb
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 18 deletions.
33 changes: 29 additions & 4 deletions libcudacxx/include/cuda/std/detail/libcxx/include/span
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ template<class R>
#include <cuda/std/array>
#include <cuda/std/cstddef> // for ptrdiff_t
#include <cuda/std/detail/libcxx/include/stdexcept>
#include <cuda/std/initializer_list>

// standard-mandated includes
#include <cuda/std/version>
Expand Down Expand Up @@ -202,6 +203,12 @@ _CCCL_INLINE_VAR constexpr bool __is_std_span<span<_Tp, _Extent>> = true;
template <class _From, class _To>
_CCCL_CONCEPT __span_array_convertible = _CCCL_TRAIT(is_convertible, _From (*)[], _To (*)[]);

template <class _Tp>
_CCCL_INLINE_VAR constexpr bool __is_std_initializer_list = false;

template <class _Tp>
_CCCL_INLINE_VAR constexpr bool __is_std_initializer_list<initializer_list<_Tp>> = true;

// We want to ensure that span interacts nicely with containers that might not have had the ranges treatment
# if defined(__cpp_lib_ranges) && !_CCCL_COMPILER(MSVC2017)
# define _CCCL_SPAN_USES_RANGES
Expand All @@ -216,7 +223,8 @@ _CCCL_CONCEPT_FRAGMENT(
requires(_CUDA_VRANGES::sized_range<_Range>),
requires((_CUDA_VRANGES::borrowed_range<_Range> || _CCCL_TRAIT(is_const, _ElementType))),
requires((!_CCCL_TRAIT(is_array, remove_cvref_t<_Range>))),
requires((!__is_std_span<remove_cvref_t<_Range>> && !__is_std_array<remove_cvref_t<_Range>>) ),
requires((!__is_std_span<remove_cvref_t<_Range>> && !__is_std_array<remove_cvref_t<_Range>>
&& !__is_std_initializer_list<remove_cvref_t<_Range>>) ),
requires(_CCCL_TRAIT(
is_convertible, remove_reference_t<_CUDA_VRANGES::range_reference_t<_Range>> (*)[], _ElementType (*)[]))));

Expand Down Expand Up @@ -259,11 +267,13 @@ _CCCL_INLINE_VAR constexpr bool __is_span_compatible_container<
_ElementType,
void_t<
// is not a specialization of span
enable_if_t<!__is_std_span<_Container>, nullptr_t>,
enable_if_t<!__is_std_span<remove_cvref_t<_Container>>, nullptr_t>,
// is not a specialization of array
enable_if_t<!__is_std_array<remove_cvref_t<_Container>>, nullptr_t>,
// is not a specialization of array
enable_if_t<!__is_std_array<_Container>, nullptr_t>,
enable_if_t<!__is_std_initializer_list<remove_cvref_t<_Container>>, nullptr_t>,
// is_array_v<Container> is false,
enable_if_t<!_CCCL_TRAIT(is_array, _Container), nullptr_t>,
enable_if_t<!_CCCL_TRAIT(is_array, remove_cvref_t<_Container>), nullptr_t>,
// data(cont) and size(cont) are well formed
decltype(_CUDA_VSTD::data(_CUDA_VSTD::declval<_Container&>())),
decltype(_CUDA_VSTD::size(_CUDA_VSTD::declval<_Container&>())),
Expand Down Expand Up @@ -329,6 +339,14 @@ public:
: __data_{nullptr}
{}

_CCCL_TEMPLATE(class _Tp2 = _Tp)
_CCCL_REQUIRES(_CCCL_TRAIT(is_const, _Tp2))
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit span(initializer_list<value_type> __il) noexcept
: __data_{__il.begin()}
{
_CCCL_ASSERT(_Extent == __il.size(), "size mismatch in span's constructor (initializer_list).");
}

_CCCL_HIDE_FROM_ABI span(const span&) noexcept = default;
_CCCL_HIDE_FROM_ABI span& operator=(const span&) noexcept = default;

Expand Down Expand Up @@ -585,6 +603,13 @@ public:
, __size_{0}
{}

_CCCL_TEMPLATE(class _Tp2 = _Tp)
_CCCL_REQUIRES(_CCCL_TRAIT(is_const, _Tp2))
_LIBCUDACXX_HIDE_FROM_ABI constexpr span(initializer_list<value_type> __il) noexcept
: __data_{__il.begin()}
, __size_{__il.size()}
{}

_CCCL_HIDE_FROM_ABI span(const span&) noexcept = default;
_CCCL_HIDE_FROM_ABI span& operator=(const span&) noexcept = default;

Expand Down
5 changes: 3 additions & 2 deletions libcudacxx/include/cuda/std/version
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@
# ifndef _LIBCUDACXX_HAS_NO_THREADS
// # define __cccl_lib_shared_timed_mutex 201402L
# endif // !_LIBCUDACXX_HAS_NO_THREADS
# define __cccl_lib_source_location 201907L
# define __cccl_lib_span 202311L
# define __cccl_lib_source_location 201907L
# define __cccl_lib_span 202311L
# define __cccl_lib_span_initializer_list 202311L
// # define __cccl_lib_string_udls 201304L
# define __cccl_lib_transformation_trait_aliases 201304L
# define __cccl_lib_transparent_operators 201210L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// — remove_pointer_t<decltype(data(arr))>(*)[] is convertible to ElementType(*)[].
//

#include <cuda/std/array>
#include <cuda/std/cassert>
#include <cuda/std/span>

Expand Down Expand Up @@ -92,8 +93,8 @@ __host__ __device__ constexpr bool testSpan()
assert(s3.data() == val && s3.size() == 2);
assert(s4.data() == val && s4.size() == 2);

cuda::std::span<const int> s5 = {{1, 2}};
cuda::std::span<const int, 2> s6 = {{1, 2}};
cuda::std::span<const int> s5 = {cuda::std::array<int, 2>{1, 2}};
cuda::std::span<const int, 2> s6 = {cuda::std::array<int, 2>{1, 2}};
assert(s5.size() == 2); // and it dangles
assert(s6.size() == 2); // and it dangles

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// 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) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11

// <span>
// UNSUPPORTED: c++11

// <cuda/std/span>

// constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il);

// #include <any>
#include <cuda/std/cassert>
#include <cuda/std/cstddef>
#include <cuda/std/initializer_list>
#include <cuda/std/span>
#include <cuda/std/type_traits>

#include "test_convertible.h"
#include "test_macros.h"

using cuda::std::is_constructible;

// Constructor constrains
static_assert(is_constructible<cuda::std::span<const int>, cuda::std::initializer_list<int>>::value, "");
static_assert(is_constructible<cuda::std::span<const int, 42>, cuda::std::initializer_list<int>>::value, "");
static_assert(!is_constructible<cuda::std::span<const int>, cuda::std::initializer_list<const int>>::value, "");
static_assert(!is_constructible<cuda::std::span<const int, 42>, cuda::std::initializer_list<const int>>::value, "");

static_assert(!is_constructible<cuda::std::span<int>, cuda::std::initializer_list<int>>::value, "");
static_assert(!is_constructible<cuda::std::span<int, 42>, cuda::std::initializer_list<int>>::value, "");
static_assert(!is_constructible<cuda::std::span<int>, cuda::std::initializer_list<const int>>::value, "");
static_assert(!is_constructible<cuda::std::span<int, 42>, cuda::std::initializer_list<const int>>::value, "");

// Constructor conditionally explicit

static_assert(!test_convertible<cuda::std::span<const int, 28>, cuda::std::initializer_list<int>>(),
"This constructor must be explicit");
static_assert(is_constructible<cuda::std::span<const int, 28>, cuda::std::initializer_list<int>>::value, "");
static_assert(test_convertible<cuda::std::span<const int>, cuda::std::initializer_list<int>>(),
"This constructor must not be explicit");
static_assert(is_constructible<cuda::std::span<const int>, cuda::std::initializer_list<int>>::value, "");

struct Sink
{
constexpr Sink() = default;
Expand All @@ -26,25 +56,61 @@ __host__ __device__ constexpr cuda::std::size_t count(cuda::std::span<const Sink
return sp.size();
}

template <int N>
__host__ __device__ constexpr cuda::std::size_t countn(cuda::std::span<const Sink, N> sp)
template <cuda::std::size_t N>
__host__ __device__ constexpr cuda::std::size_t count_n(cuda::std::span<const Sink, N> sp)
{
return sp.size();
}

__host__ __device__ constexpr bool test()
{
Sink a[10] = {};
assert(count({a}) == 10);
assert(count({a, a + 10}) == 10);
assert(countn<10>({a}) == 10);
// Dynamic extent
{
Sink a[10]{};

assert(count({a}) == 1);
assert(count({a, a + 10}) == 2);
assert(count({a, a + 1, a + 2}) == 3);
assert(count(cuda::std::initializer_list<Sink>{a[0], a[1], a[2], a[3]}) == 4);
}

return true;
}

// Test P2447R4 "Annex C examples"

__host__ __device__ constexpr int three(cuda::std::span<void* const> sp)
{
return static_cast<int>(sp.size());
}

__host__ __device__ bool test_P2447R4_annex_c_examples()
{
// 1. Overload resolution is affected
// --> tested in "initializer_list.verify.cpp"

// 2. The `initializer_list` ctor has high precedence
// --> tested in "initializer_list.verify.cpp"

// 3. Implicit two-argument construction with a highly convertible value_type
{
void* a[10];
assert(three({a, 0}) == 2);
}
// {
// cuda::std::any a[10];
// assert(four({a, a + 10}) == 2);
// }

return true;
}

int main(int, char**)
{
test();
assert(test());
static_assert(test(), "");

assert(test_P2447R4_annex_c_examples());

return 0;
}

0 comments on commit 4ae70bb

Please sign in to comment.