diff --git a/cpp/include/cudf/column/column_view.hpp b/cpp/include/cudf/column/column_view.hpp index 76d5709d869..e6980411a11 100644 --- a/cpp/include/cudf/column/column_view.hpp +++ b/cpp/include/cudf/column/column_view.hpp @@ -11,6 +11,8 @@ #include #include +#include + #include #include #include @@ -450,6 +452,26 @@ class column_view : public detail::column_view_base { return device_span(data(), size()); } + /** + * @brief Converts a column view into a cuda::std::span. + * + * Only numeric and chrono data types are supported. The column view must not + * be nullable. + * + * @tparam T The device span type. Must be const and match the column view's type. + * @throws cudf::logic_error if the column view type does not match the span type. + * @throws cudf::logic_error if the column view is nullable. + * @return A span of the column view's data + */ + template () or cudf::is_chrono())> + [[nodiscard]] operator cuda::std::span() const + { + CUDF_EXPECTS(type() == cudf::data_type{cudf::type_to_id()}, + "Span type must match column view type"); + CUDF_EXPECTS(!nullable(), "A nullable column view cannot be converted to a span."); + return cuda::std::span(data(), size()); + } + protected: /** * @brief Returns pointer to the base device memory allocation. diff --git a/cpp/tests/column/column_view_device_span_test.cpp b/cpp/tests/column/column_view_device_span_test.cpp index f8f180dfd80..75691b79f20 100644 --- a/cpp/tests/column/column_view_device_span_test.cpp +++ b/cpp/tests/column/column_view_device_span_test.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -27,12 +28,12 @@ std::unique_ptr example_column() } template -struct ColumnViewDeviceSpanTests : public cudf::test::BaseFixture {}; +struct ColumnViewSpanTests : public cudf::test::BaseFixture {}; using DeviceSpanTypes = cudf::test::FixedWidthTypesWithoutFixedPoint; -TYPED_TEST_SUITE(ColumnViewDeviceSpanTests, DeviceSpanTypes); +TYPED_TEST_SUITE(ColumnViewSpanTests, DeviceSpanTypes); -TYPED_TEST(ColumnViewDeviceSpanTests, conversion_round_trip) +TYPED_TEST(ColumnViewSpanTests, device_span_conversion_round_trip) { auto col = example_column(); auto col_view = cudf::column_view{*col}; @@ -43,19 +44,74 @@ TYPED_TEST(ColumnViewDeviceSpanTests, conversion_round_trip) CUDF_TEST_EXPECT_COLUMNS_EQUAL(col_view, col_view_from_device_span); } -struct ColumnViewDeviceSpanErrorTests : public cudf::test::BaseFixture {}; +struct ColumnViewSpanErrorTests : public cudf::test::BaseFixture {}; -TEST_F(ColumnViewDeviceSpanErrorTests, type_mismatch) +TEST_F(ColumnViewSpanErrorTests, device_span_type_mismatch) { auto col = example_column(); auto col_view = cudf::column_view{*col}; EXPECT_THROW((void)cudf::device_span{col_view}, cudf::logic_error); } -TEST_F(ColumnViewDeviceSpanErrorTests, nullable_column) +TEST_F(ColumnViewSpanErrorTests, device_span_nullable_column) { auto col = example_column(); col->set_null_mask(cudf::create_null_mask(col->size(), cudf::mask_state::ALL_NULL), col->size()); auto col_view = cudf::column_view{*col}; EXPECT_THROW((void)cudf::device_span{col_view}, cudf::logic_error); } + +TYPED_TEST(ColumnViewSpanTests, std_span_conversion_to_span) +{ + auto col = example_column(); + auto col_view = cudf::column_view{*col}; + + // Test implicit conversion to cuda::std::span + cuda::std::span cuda_span_from_col_view = col_view; + + // Verify span properties match column view + EXPECT_EQ(cuda_span_from_col_view.size(), static_cast(col_view.size())); + EXPECT_EQ(cuda_span_from_col_view.data(), col_view.data()); + EXPECT_FALSE(cuda_span_from_col_view.empty()); +} + +TYPED_TEST(ColumnViewSpanTests, std_span_explicit_conversion_to_span) +{ + auto col = example_column(); + auto col_view = cudf::column_view{*col}; + + // Test explicit conversion to cuda::std::span + auto cuda_span_from_col_view = static_cast>(col_view); + + // Verify span properties match column view + EXPECT_EQ(cuda_span_from_col_view.size(), static_cast(col_view.size())); + EXPECT_EQ(cuda_span_from_col_view.data(), col_view.data()); +} + +TYPED_TEST(ColumnViewSpanTests, std_span_empty_column_to_span) +{ + cudf::test::fixed_width_column_wrapper empty_col{}; + auto col_view = cudf::column_view{empty_col}; + + // Test conversion of empty column to cuda::std::span + cuda::std::span cuda_span_from_col_view = col_view; + + // Verify span properties for empty column + EXPECT_EQ(cuda_span_from_col_view.size(), 0u); + EXPECT_TRUE(cuda_span_from_col_view.empty()); +} + +TEST_F(ColumnViewSpanErrorTests, std_span_type_mismatch) +{ + auto col = example_column(); + auto col_view = cudf::column_view{*col}; + EXPECT_THROW((void)cuda::std::span{col_view}, cudf::logic_error); +} + +TEST_F(ColumnViewSpanErrorTests, std_span_nullable_column) +{ + auto col = example_column(); + col->set_null_mask(cudf::create_null_mask(col->size(), cudf::mask_state::ALL_NULL), col->size()); + auto col_view = cudf::column_view{*col}; + EXPECT_THROW((void)cuda::std::span{col_view}, cudf::logic_error); +}