Skip to content

Implement operator<< for cuda::std::string_view #4736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions libcudacxx/include/cuda/std/__string/helper_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

_LIBCUDACXX_BEGIN_NAMESPACE_STD

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
Comment on lines 34 to 36
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding _CCCL_EXEC_CHECK_DISABLE is necessary to allow using e. g. host only char_traits

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit torn here, we should use host only char_traits as they will crash.

I believe the solution with just casting to ::std::string_view is the better solution

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that the user can pass any type traits he wants, they might be host only, or device only..

_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT __pos) noexcept
Expand All @@ -48,6 +49,7 @@ __cccl_str_find(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT __pos) noexce
return static_cast<_SizeT>(__r - __p);
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _Traits>
_LIBCUDACXX_HIDE_FROM_ABI constexpr const _CharT*
__cccl_search_substring(const _CharT* __first1, const _CharT* __last1, const _CharT* __first2, const _CharT* __last2)
Expand Down Expand Up @@ -121,6 +123,7 @@ __cccl_str_find(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos,
return static_cast<_SizeT>(__r - __p);
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_rfind(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT __pos) noexcept
Expand All @@ -147,6 +150,7 @@ __cccl_str_rfind(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT __pos) noexc
return __npos;
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_rfind(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos, _SizeT __n) noexcept
Expand All @@ -169,6 +173,7 @@ __cccl_str_rfind(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos
return static_cast<_SizeT>(__r - __p);
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find_first_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos, _SizeT __n) noexcept
Expand All @@ -185,6 +190,7 @@ __cccl_str_find_first_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _Siz
return static_cast<_SizeT>(__r - __p);
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find_last_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos, _SizeT __n) noexcept
Expand All @@ -211,6 +217,7 @@ __cccl_str_find_last_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _Size
return __npos;
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find_first_not_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos, _SizeT __n) noexcept
Expand All @@ -230,6 +237,7 @@ __cccl_str_find_first_not_of(const _CharT* __p, _SizeT __sz, const _CharT* __s,
return __npos;
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find_first_not_of(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT __pos) noexcept
Expand All @@ -248,6 +256,7 @@ __cccl_str_find_first_not_of(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT
return __npos;
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find_last_not_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _SizeT __pos, _SizeT __n) noexcept
Expand All @@ -270,6 +279,7 @@ __cccl_str_find_last_not_of(const _CharT* __p, _SizeT __sz, const _CharT* __s, _
return __npos;
}

_CCCL_EXEC_CHECK_DISABLE
template <class _CharT, class _SizeT, class _Traits, _SizeT __npos>
_LIBCUDACXX_HIDE_FROM_ABI constexpr _SizeT
__cccl_str_find_last_not_of(const _CharT* __p, _SizeT __sz, _CharT __c, _SizeT __pos) noexcept
Expand Down
26 changes: 21 additions & 5 deletions libcudacxx/include/cuda/std/string_view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
#include <cuda/std/version>

#if !_CCCL_COMPILER(NVRTC)
# include <iosfwd>
# include <string_view>
#endif // !_CCCL_COMPILER(NVRTC)

#include <cuda/std/__cccl/prologue.h>
Expand Down Expand Up @@ -105,6 +105,7 @@ public:

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

_CCCL_EXEC_CHECK_DISABLE
_LIBCUDACXX_HIDE_FROM_ABI constexpr basic_string_view(const _CharT* __s) noexcept
: __data_{__s}
, __size_{_Traits::length(__s)}
Expand Down Expand Up @@ -262,6 +263,7 @@ public:
__other.__size_ = __sz;
}

_CCCL_EXEC_CHECK_DISABLE
_LIBCUDACXX_HIDE_FROM_ABI constexpr size_type copy(_CharT* __s, size_type __n, size_type __pos = 0) const
{
if (__pos > __size_)
Expand All @@ -288,6 +290,7 @@ public:

// compare

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr int compare(basic_string_view __sv) const noexcept
{
const auto __rlen = _CUDA_VSTD::min(__size_, __sv.__size_);
Expand Down Expand Up @@ -350,6 +353,7 @@ public:
return _CUDA_VSTD::__cccl_str_find<value_type, size_type, traits_type, npos>(__data_, __size_, __s, __pos, __n);
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr size_type find(const _CharT* __s, size_type __pos = 0) const noexcept
{
_CCCL_ASSERT(__s != nullptr, "string_view::find(): received nullptr");
Expand Down Expand Up @@ -379,6 +383,7 @@ public:
return _CUDA_VSTD::__cccl_str_rfind<value_type, size_type, traits_type, npos>(__data_, __size_, __s, __pos, __n);
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr size_type
rfind(const _CharT* __s, size_type __pos = npos) const noexcept
{
Expand Down Expand Up @@ -411,6 +416,7 @@ public:
__data_, __size_, __s, __pos, __n);
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr size_type
find_first_of(const _CharT* __s, size_type __pos = 0) const noexcept
{
Expand Down Expand Up @@ -443,6 +449,7 @@ public:
__data_, __size_, __s, __pos, __n);
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr size_type
find_last_of(const _CharT* __s, size_type __pos = npos) const noexcept
{
Expand Down Expand Up @@ -476,6 +483,7 @@ public:
__data_, __size_, __s, __pos, __n);
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr size_type
find_first_not_of(const _CharT* __s, size_type __pos = 0) const noexcept
{
Expand Down Expand Up @@ -509,6 +517,7 @@ public:
__data_, __size_, __s, __pos, __n);
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr size_type
find_last_not_of(const _CharT* __s, size_type __pos = npos) const noexcept
{
Expand All @@ -524,6 +533,7 @@ public:
return (__size_ >= __s.__size_) && compare(0, __s.__size_, __s) == 0;
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr bool starts_with(value_type __c) const noexcept
{
return (__size_ > 0) && _Traits::eq(front(), __c);
Expand All @@ -541,6 +551,7 @@ public:
return (__size_ >= __s.__size_) && compare(__size_ - __s.__size_, npos, __s) == 0;
}

_CCCL_EXEC_CHECK_DISABLE
[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr bool ends_with(value_type __c) const noexcept
{
return (__size_ > 0) && _Traits::eq(back(), __c);
Expand Down Expand Up @@ -740,14 +751,19 @@ _CCCL_HOST_DEVICE basic_string_view(_Range&&) -> basic_string_view<_CUDA_VRANGES

// operator <<

#if 0 // todo: we need to implement char_traits stream types & functions
template <class _CharT, class _Traits>
_LIBCUDACXX_HIDE_FROM_ABI ::std::basic_ostream<_CharT, _Traits>&
_CCCL_HIDE_FROM_ABI _CCCL_HOST ::std::basic_ostream<_CharT, _Traits>&
operator<<(::std::basic_ostream<_CharT, _Traits>& __os, basic_string_view<_CharT, _Traits> __str)
{
return __os.write(__str.data(), static_cast<::std::streamsize>(__str.size()));
return __os << ::std::basic_string_view<_CharT, _Traits>{__str.data(), __str.size()};
}

template <class _CharT>
_CCCL_HIDE_FROM_ABI _CCCL_HOST ::std::basic_ostream<_CharT, ::std::char_traits<_CharT>>& operator<<(
::std::basic_ostream<_CharT, ::std::char_traits<_CharT>>& __os, basic_string_view<_CharT, char_traits<_CharT>> __str)
{
return __os << ::std::basic_string_view<_CharT, ::std::char_traits<_CharT>>{__str.data(), __str.size()};
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had to split the implementation to map the cuda::std::char_traits to std::char_traits

#endif // 0

// literals

Expand Down
5 changes: 1 addition & 4 deletions libcudacxx/include/cuda/std/version
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
// # define __cccl_lib_shared_mutex 201505L
// # define __cccl_lib_shared_ptr_arrays 201611L
// # define __cccl_lib_shared_ptr_weak_type 201606L
// # define __cccl_lib_string_view 201606L
#define __cccl_lib_string_view 201803L
// # define __cccl_lib_to_chars 201611L
// # define __cccl_lib_uncaught_exceptions 201411L
// # define __cccl_lib_unordered_map_try_emplace 201411L
Expand Down Expand Up @@ -173,7 +173,6 @@
// # define __cccl_lib_constexpr_misc 201811L
// # define __cccl_lib_constexpr_numeric 201911L
// # define __cccl_lib_constexpr_string 201907L
// # define __cccl_lib_constexpr_string_view 201811L
// # define __cccl_lib_constexpr_swap_algorithms 201806L
// # define __cccl_lib_constexpr_tuple 201811L
// # define __cccl_lib_constexpr_utility 201811L
Expand Down Expand Up @@ -206,8 +205,6 @@
// # define __cccl_lib_source_location 201907L
// # define __cccl_lib_ssize 201902L
// # define __cccl_lib_starts_ends_with 201711L
// # undef __cccl_lib_string_view
// # define __cccl_lib_string_view 201803L
// # define __cccl_lib_syncbuf 201803L
// # define __cccl_lib_three_way_comparison 201907L
# define __cccl_lib_unwrap_ref 201811L
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//===----------------------------------------------------------------------===//
//
// 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) 2025 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//

// <cuda/std/string_view>

// template<class charT, class traits, class Allocator>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>& os,
// const basic_string_view<charT,traits> str);

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

#include <sstream>

#include "literal.h"

template <class CharT>
void test_with_default_type_traits()
{
using OS = std::basic_ostringstream<CharT>;
using SV = cuda::std::basic_string_view<CharT>;

// check that cuda::std::char_traits are mapped to std::char_traits
static_assert(cuda::std::is_same_v<typename OS::char_type, CharT>);
static_assert(cuda::std::is_same_v<typename OS::traits_type, std::char_traits<CharT>>);
static_assert(cuda::std::is_same_v<typename SV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename SV::traits_type, cuda::std::char_traits<CharT>>);

const CharT* str = TEST_STRLIT(CharT, "some text");

// 1. test basic write without formatting
{
OS out{};
SV sv{str};

out << sv;
assert(out.good());
assert(out.str() == str);
}

// 2. test basic write with formatting
{
OS out{};
SV sv{str};

out.width(12);
out << sv;
assert(out.good());
assert(out.str() == TEST_STRLIT(CharT, " some text"));
}
}

template <class CharT>
struct custom_type_traits : std::char_traits<CharT>
{};

template <class CharT>
void test_with_custom_type_traits()
{
using OS = std::basic_ostringstream<CharT, custom_type_traits<CharT>>;
using SV = cuda::std::basic_string_view<CharT, custom_type_traits<CharT>>;

// check that cuda::std::char_traits are mapped to std::char_traits
static_assert(cuda::std::is_same_v<typename OS::char_type, CharT>);
static_assert(cuda::std::is_same_v<typename OS::traits_type, custom_type_traits<CharT>>);
static_assert(cuda::std::is_same_v<typename SV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename SV::traits_type, custom_type_traits<CharT>>);

const CharT* str = TEST_STRLIT(CharT, "some text");

// 1. test basic write without formatting
{
OS out{};
SV sv{str};

out << sv;
assert(out.good());
assert(out.str() == str);
}

// 2. test basic write with formatting
{
OS out{};
SV sv{str};

out.width(12);
out << sv;
assert(out.good());
assert(out.str() == TEST_STRLIT(CharT, " some text"));
}
}

template <class CharT>
void test_type()
{
test_with_default_type_traits<CharT>();
test_with_custom_type_traits<CharT>();
}

void test()
{
test_type<char>();
#if _CCCL_HAS_WCHAR_T()
test_type<wchar_t>();
#endif // _CCCL_HAS_WCHAR_T()
}

int main(int, char**)
{
NV_IF_TARGET(NV_IS_HOST, (test();))
return 0;
}
Loading