Skip to content

Commit

Permalink
Implement ranges::zip_view
Browse files Browse the repository at this point in the history
  • Loading branch information
miscco committed Jul 7, 2023
1 parent d666cea commit 60e823e
Show file tree
Hide file tree
Showing 56 changed files with 8,488 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM 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, c++14

// constexpr auto begin() requires (!(simple-view<Views> && ...));
// constexpr auto begin() const requires (range<const Views> && ...);

#include <cuda/std/ranges>

#include <cuda/std/cassert>
#include <cuda/std/concepts>
#include <cuda/std/tuple>
#include <cuda/std/utility>

#include "test_macros.h"
#include "types.h"

#if TEST_STD_VER > 17
template <class T>
concept HasConstBegin = requires(const T& ct) { ct.begin(); };

template <class T>
concept HasBegin = requires(T& t) { t.begin(); };

template <class T>
concept HasConstAndNonConstBegin =
HasConstBegin<T> &&
requires(T& t, const T& ct) { requires !cuda::std::same_as<decltype(t.begin()), decltype(ct.begin())>; };

template <class T>
concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;

template <class T>
concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
#else
template <class T, class = void>
inline constexpr bool HasConstBegin = false;

template <class T>
inline constexpr bool HasConstBegin<T,
cuda::std::void_t<decltype(cuda::std::declval<const T&>().begin())>> = true;

template <class T, class = void>
inline constexpr bool HasBegin = false;

template <class T>
inline constexpr bool HasBegin<T,
cuda::std::void_t<decltype(cuda::std::declval<T&>().begin())>> = true;

template <class T, class = void>
inline constexpr bool HasConstAndNonConstBegin = false;

template <class T>
inline constexpr bool HasConstAndNonConstBegin<T,
cuda::std::void_t<cuda::std::enable_if_t<!cuda::std::same_as<decltype(cuda::std::declval<T&>().begin()),
decltype(cuda::std::declval<const T&>().begin())>>>> = true;

template <class T>
inline constexpr bool HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;

template <class T>
inline constexpr bool HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
#endif

struct NoConstBeginView : cuda::std::ranges::view_base {
__host__ __device__ int* begin() { return nullptr; }
__host__ __device__ int* end() { return nullptr; }
};

__host__ __device__ constexpr bool test() {
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
{
// all underlying iterators should be at the begin position
cuda::std::ranges::zip_view v(SizedRandomAccessView{buffer}, cuda::std::views::iota(0), cuda::std::ranges::single_view(2.));
decltype(auto) val = *v.begin();
static_assert(cuda::std::same_as<decltype(val), cuda::std::tuple<int&, int, double&>>);
assert(val == cuda::std::make_tuple(1, 0, 2.0));
assert(&(cuda::std::get<0>(val)) == &buffer[0]);
}

{
// with empty range
cuda::std::ranges::zip_view v(SizedRandomAccessView{buffer}, cuda::std::ranges::empty_view<int>());
assert(v.begin() == v.end());
}

{
// underlying ranges all model simple-view
cuda::std::ranges::zip_view v(SimpleCommon{buffer}, SimpleCommon{buffer});
static_assert(cuda::std::is_same_v<decltype(v.begin()), decltype(cuda::std::as_const(v).begin())>);
assert(v.begin() == cuda::std::as_const(v).begin());
auto [x, y] = *cuda::std::as_const(v).begin();
assert(&x == &buffer[0]);
assert(&y == &buffer[0]);

using View = decltype(v);
static_assert(HasOnlyConstBegin<View>);
static_assert(!HasOnlyNonConstBegin<View>);
static_assert(!HasConstAndNonConstBegin<View>);
}

{
// not all underlying ranges model simple-view
cuda::std::ranges::zip_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer});
static_assert(!cuda::std::is_same_v<decltype(v.begin()), decltype(cuda::std::as_const(v).begin())>);
assert(v.begin() == cuda::std::as_const(v).begin());
auto [x, y] = *cuda::std::as_const(v).begin();
assert(&x == &buffer[0]);
assert(&y == &buffer[0]);

using View = decltype(v);
static_assert(!HasOnlyConstBegin<View>);
static_assert(!HasOnlyNonConstBegin<View>);
static_assert(HasConstAndNonConstBegin<View>);
}

{
// underlying const R is not a range
using View = cuda::std::ranges::zip_view<SimpleCommon, NoConstBeginView>;
static_assert(!HasOnlyConstBegin<View>);
static_assert(HasOnlyNonConstBegin<View>);
static_assert(!HasConstAndNonConstBegin<View>);
}
return true;
}

int main(int, char**) {
test();
#if TEST_STD_VER > 17 && defined(_LIBCUDACXX_ADDRESSOF)
static_assert(test(), "");
#endif // TEST_STD_VER > 17 && defined(_LIBCUDACXX_ADDRESSOF)

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM 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, c++14

// template<class... Views>
// inline constexpr bool enable_borrowed_range<zip_view<Views...>> =
// (enable_borrowed_range<Views> && ...);

#include <cuda/std/ranges>
#include <cuda/std/tuple>

struct Borrowed : cuda::std::ranges::view_base {
__host__ __device__ int* begin() const { return nullptr; }
__host__ __device__ int* end() const { return nullptr; }
};

template <>
inline constexpr bool cuda::std::ranges::enable_borrowed_range<Borrowed> = true;

static_assert(cuda::std::ranges::borrowed_range<Borrowed>);

struct NonBorrowed : cuda::std::ranges::view_base {
__host__ __device__ int* begin() const { return nullptr; }
__host__ __device__ int* end() const { return nullptr; }
};
static_assert(!cuda::std::ranges::borrowed_range<NonBorrowed>);

// test borrowed_range
static_assert(cuda::std::ranges::borrowed_range<cuda::std::ranges::zip_view<Borrowed>>);
static_assert(cuda::std::ranges::borrowed_range<cuda::std::ranges::zip_view<Borrowed, Borrowed>>);
static_assert(!cuda::std::ranges::borrowed_range<cuda::std::ranges::zip_view<Borrowed, NonBorrowed>>);
static_assert(!cuda::std::ranges::borrowed_range<cuda::std::ranges::zip_view<NonBorrowed>>);
static_assert(!cuda::std::ranges::borrowed_range<cuda::std::ranges::zip_view<NonBorrowed, NonBorrowed>>);

int main(int, char**) {
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM 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, c++14

// cuda::std::views::zip

#include <cuda/std/ranges>

#include <cuda/std/array>
#include <cuda/std/cassert>
#include <cuda/std/tuple>
#include <cuda/std/type_traits>
#include <cuda/std/utility>

#include "test_macros.h"
#include "types.h"

static_assert(cuda::std::is_invocable_v<decltype((cuda::std::views::zip))>);
static_assert(!cuda::std::is_invocable_v<decltype((cuda::std::views::zip)), int>);
static_assert(cuda::std::is_invocable_v<decltype((cuda::std::views::zip)), SizedRandomAccessView>);
static_assert(
cuda::std::is_invocable_v<decltype((cuda::std::views::zip)), SizedRandomAccessView, cuda::std::ranges::iota_view<int, int>>);
static_assert(!cuda::std::is_invocable_v<decltype((cuda::std::views::zip)), SizedRandomAccessView, int>);

__host__ __device__ constexpr bool test() {
{
// zip zero arguments
auto v = cuda::std::views::zip();
assert(cuda::std::ranges::empty(v));
static_assert(cuda::std::is_same_v<decltype(v), cuda::std::ranges::empty_view<cuda::std::tuple<>>>);
}

{
// zip a view
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
decltype(auto) v = cuda::std::views::zip(SizedRandomAccessView{buffer});
static_assert(cuda::std::same_as<decltype(v), cuda::std::ranges::zip_view<SizedRandomAccessView>>);
assert(cuda::std::ranges::size(v) == 8);
static_assert(cuda::std::is_same_v<cuda::std::ranges::range_reference_t<decltype(v)>, cuda::std::tuple<int&>>);
}

{
// zip a viewable range
cuda::std::array a{1, 2, 3};
decltype(auto) v = cuda::std::views::zip(a);
static_assert(cuda::std::same_as<decltype(v), cuda::std::ranges::zip_view<cuda::std::ranges::ref_view<cuda::std::array<int, 3>>>>);
assert(&(cuda::std::get<0>(*v.begin())) == &(a[0]));
static_assert(cuda::std::is_same_v<cuda::std::ranges::range_reference_t<decltype(v)>, cuda::std::tuple<int&>>);
}

{
// zip the zip_view
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
decltype(auto) v = cuda::std::views::zip(SizedRandomAccessView{buffer}, SizedRandomAccessView{buffer});
static_assert(cuda::std::same_as<decltype(v), cuda::std::ranges::zip_view<SizedRandomAccessView, SizedRandomAccessView>>);

decltype(auto) v2 = cuda::std::views::zip(v);
static_assert(cuda::std::same_as<decltype(v2), cuda::std::ranges::zip_view<cuda::std::ranges::zip_view<SizedRandomAccessView, SizedRandomAccessView>>>);

static_assert(cuda::std::is_same_v<cuda::std::ranges::range_reference_t<decltype(v2)>, cuda::std::tuple<cuda::std::pair<int&, int&>>>);
unused(v2);
}
return true;
}

int main(int, char**) {
test();
#if defined(_LIBCUDACXX_ADDRESSOF)
static_assert(test(), "");
#endif // defined(_LIBCUDACXX_ADDRESSOF)

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM 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, c++14

// template <class... Rs>
// zip_view(Rs&&...) -> zip_view<views::all_t<Rs>...>;

#include <cuda/std/cassert>
#include <cuda/std/ranges>
#include <cuda/std/utility>

#include "test_macros.h"

struct Container {
__host__ __device__ int* begin() const;
__host__ __device__ int* end() const;
};

struct View : cuda::std::ranges::view_base {
__host__ __device__ int* begin() const;
__host__ __device__ int* end() const;
};

// GCC really does not like those inside the function
using result_zip_owning_container = cuda::std::ranges::zip_view<cuda::std::ranges::owning_view<Container>>;
using result_zip_owning_container_view = cuda::std::ranges::zip_view<cuda::std::ranges::owning_view<Container>, View>;
using result_zip_ref_container_view = cuda::std::ranges::zip_view<cuda::std::ranges::owning_view<Container>, View, cuda::std::ranges::ref_view<Container>>;

__host__ __device__ void testCTAD() {
static_assert(cuda::std::is_same_v<decltype(cuda::std::ranges::zip_view(Container{})),
result_zip_owning_container>);

static_assert(cuda::std::is_same_v<decltype(cuda::std::ranges::zip_view(Container{}, View{})),
result_zip_owning_container_view>);

Container c{};
static_assert(cuda::std::is_same_v<
decltype(cuda::std::ranges::zip_view(Container{}, View{}, c)),
result_zip_ref_container_view>);
unused(c);
}

int main(int, char**) {
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM 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, c++14

// zip_view() = default;

#include <cuda/std/ranges>

#include <cuda/std/cassert>
#include <cuda/std/type_traits>
#include <cuda/std/utility>

#include "test_macros.h"

STATIC_TEST_GLOBAL_VAR constexpr int buff[] = {1, 2, 3};

struct DefaultConstructibleView : cuda::std::ranges::view_base {
__host__ __device__ constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {}
__host__ __device__ constexpr int const* begin() const { return begin_; }
__host__ __device__ constexpr int const* end() const { return end_; }

private:
int const* begin_;
int const* end_;
};

struct NoDefaultCtrView : cuda::std::ranges::view_base {
NoDefaultCtrView() = delete;
__host__ __device__ int* begin() const;
__host__ __device__ int* end() const;
};

// The default constructor requires all underlying views to be default constructible.
// It is implicitly required by the tuple's constructor. If any of the iterators are
// not default constructible, zip iterator's =default would be implicitly deleted.
static_assert(cuda::std::is_default_constructible_v<cuda::std::ranges::zip_view<DefaultConstructibleView>>);
static_assert(
cuda::std::is_default_constructible_v<cuda::std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>>);
static_assert(!cuda::std::is_default_constructible_v<cuda::std::ranges::zip_view<DefaultConstructibleView, NoDefaultCtrView>>);
static_assert(!cuda::std::is_default_constructible_v<cuda::std::ranges::zip_view<NoDefaultCtrView, NoDefaultCtrView>>);
static_assert(!cuda::std::is_default_constructible_v<cuda::std::ranges::zip_view<NoDefaultCtrView>>);

__host__ __device__ constexpr bool test() {
{
using View = cuda::std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>;
View v = View(); // the default constructor is not explicit
assert(v.size() == 3);
auto it = v.begin();
using Pair = cuda::std::pair<const int&, const int&>;
assert(*it++ == Pair(buff[0], buff[0]));
assert(*it++ == Pair(buff[1], buff[1]));
assert(*it == Pair(buff[2], buff[2]));
}

return true;
}

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

return 0;
}
Loading

0 comments on commit 60e823e

Please sign in to comment.