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
44 changes: 42 additions & 2 deletions libcudacxx/include/cuda/std/string_view
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ public:
, __size_{_CUDA_VRANGES::size(__r)}
{}

#if !_CCCL_COMPILER(NVRTC)
_CCCL_HIDE_FROM_ABI constexpr basic_string_view(::std::basic_string_view<_CharT, _Traits> __sv) noexcept
Copy link
Contributor

Choose a reason for hiding this comment

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

This forces use of the same char_traits than this string_view is using. We should templatize this so that we can construct from a ::std::string_view

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is intentional, the idea is that both char traits types must match with an exception of std::char_traits that are mapped to cuda::std::char_traits. That means that:

  • cuda::std::basic_string_view<CharT, cuda::std::char_traits<CharT>> can be constructed (converted) from (to) both std::basic_string_view<CharT, std::char_traits<CharT>> and std::basic_string_view<CharT, cuda::std::char_traits<CharT>>
  • cuda::std::basic_string_view<CharT, Traits> can be constructed (converted) from (to) std::basic_string_view<CharT, Traits>

Do you think this is wrong?

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe then should have 2 constructors,

  • basic_string_view(::std::basic_string_view<_CharT, _Traits> __sv)
  • basic_string_view(::std::basic_string_view<_CharT, ::std::char_traits<_CharT>> __sv)

We can then omit the constraint on the second constructor

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's what we have now. But I don't think we can omit the constraint, because we must ensure that the current type traits are cuda::std::char_traits, otherwise we would allow something like:

cuda::std::basic_string_view<my_char, my_char_traits>{std::basic_string_view<my_char, std::char_traits<my_char>>{...}};

which is wrong

Copy link
Contributor

Choose a reason for hiding this comment

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

I dont think so, the incoming type traits are correct and its the users responsibility to ensure that their char_traits are conforming.

Also no one writes their own char_traits, so I believe its more of a theoretical question

: __data_{__sv.data()}
, __size_{__sv.size()}
{}

_CCCL_TEMPLATE(bool = true)
_CCCL_REQUIRES(is_same_v<_Traits, char_traits<_CharT>>)
_CCCL_HIDE_FROM_ABI constexpr basic_string_view(
::std::basic_string_view<_CharT, ::std::char_traits<_CharT>> __sv) noexcept
: __data_{__sv.data()}
, __size_{__sv.size()}
{}
#endif // !_CCCL_COMPILER(NVRTC)

[[nodiscard]] _LIBCUDACXX_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept
{
return const_iterator{__data_};
Expand Down Expand Up @@ -722,6 +737,20 @@ public:
return __lhs.compare(__rhs) >= 0;
}

#if !_CCCL_COMPILER(NVRTC)
_CCCL_HIDE_FROM_ABI constexpr operator ::std::basic_string_view<_CharT, _Traits>() const noexcept
{
return ::std::basic_string_view<_CharT, _Traits>{__data_, __size_};
}

_CCCL_TEMPLATE(bool = true)
_CCCL_REQUIRES(is_same_v<_Traits, char_traits<_CharT>>)
_CCCL_HIDE_FROM_ABI constexpr operator ::std::basic_string_view<_CharT, ::std::char_traits<_CharT>>() const noexcept
{
return ::std::basic_string_view<_CharT, ::std::char_traits<_CharT>>{__data_, __size_};
}
#endif // !_CCCL_COMPILER(NVRTC)

private:
enum class __assume_valid
{
Expand Down Expand Up @@ -749,21 +778,32 @@ _CCCL_TEMPLATE(class _Range)
_CCCL_REQUIRES(_CUDA_VRANGES::contiguous_range<_Range>)
_CCCL_HOST_DEVICE basic_string_view(_Range&&) -> basic_string_view<_CUDA_VRANGES::range_value_t<_Range>>;

#if !_CCCL_COMPILER(NVRTC)
_CCCL_TEMPLATE(class _CharT, class _Traits)
_CCCL_HOST_DEVICE basic_string_view(::std::basic_string_view<_CharT, _Traits>) -> basic_string_view<_CharT, _Traits>;

_CCCL_TEMPLATE(class _CharT)
_CCCL_HOST_DEVICE basic_string_view(::std::basic_string_view<_CharT, ::std::char_traits<_CharT>>)
-> basic_string_view<_CharT, char_traits<_CharT>>;
#endif // !_CCCL_COMPILER(NVRTC)

// operator <<

#if !_CCCL_COMPILER(NVRTC)
template <class _CharT, class _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 << ::std::basic_string_view<_CharT, _Traits>{__str.data(), __str.size()};
return __os << ::std::basic_string_view<_CharT, _Traits>{__str};
}

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()};
return __os << ::std::basic_string_view<_CharT, ::std::char_traits<_CharT>>{__str};
}
#endif // !_CCCL_COMPILER(NVRTC)

// literals

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// <cuda/std/string_view>

// constexpr basic_string_view(std::basic_string_view sv);

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

#include <string_view>

#include "literal.h"

template <class CharT>
constexpr void test_with_default_type_traits()
{
using HostSV = std::basic_string_view<CharT>;
using CudaSV = 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 HostSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename HostSV::traits_type, std::char_traits<CharT>>);
static_assert(cuda::std::is_same_v<typename CudaSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename CudaSV::traits_type, cuda::std::char_traits<CharT>>);

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

// test construction from std::basic_string_view<CharT, std::char_traits<CharT>>
{
static_assert(cuda::std::is_constructible_v<CudaSV, HostSV>);
static_assert(noexcept(CudaSV{HostSV{}}));

HostSV host_sv{str};
CudaSV cuda_sv{host_sv};
assert(cuda_sv.data() == host_sv.data());
assert(cuda_sv.size() == host_sv.size());
}
}

template <class CharT>
struct custom_type_traits
: private std::char_traits<CharT>
, private cuda::std::char_traits<CharT>
{
using cuda::std::char_traits<CharT>::char_type;
using cuda::std::char_traits<CharT>::int_type;
using std::char_traits<CharT>::pos_type;
using std::char_traits<CharT>::off_type;
using std::char_traits<CharT>::state_type;

using cuda::std::char_traits<CharT>::assign;
using cuda::std::char_traits<CharT>::eq;
using cuda::std::char_traits<CharT>::lt;
using cuda::std::char_traits<CharT>::compare;
using cuda::std::char_traits<CharT>::length;
using cuda::std::char_traits<CharT>::find;
using cuda::std::char_traits<CharT>::move;
using cuda::std::char_traits<CharT>::copy;
using cuda::std::char_traits<CharT>::to_char_type;
using cuda::std::char_traits<CharT>::to_int_type;
using cuda::std::char_traits<CharT>::eq_int_type;
using std::char_traits<CharT>::eof;
using std::char_traits<CharT>::not_eof;
};

template <class CharT>
constexpr void test_with_custom_type_traits()
{
using HostSV = std::basic_string_view<CharT, custom_type_traits<CharT>>;
using CudaSV = 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 HostSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename HostSV::traits_type, custom_type_traits<CharT>>);
static_assert(cuda::std::is_same_v<typename CudaSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename CudaSV::traits_type, custom_type_traits<CharT>>);

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

// test construction from std::basic_string_view<CharT, custom_type_traits<CharT>>
{
static_assert(cuda::std::is_constructible_v<CudaSV, HostSV>);
static_assert(noexcept(CudaSV{HostSV{}}));

HostSV host_sv{str};
CudaSV cuda_sv{host_sv};
assert(cuda_sv.data() == host_sv.data());
assert(cuda_sv.size() == host_sv.size());
}
}

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

constexpr bool test()
{
test_type<char>();
#if _CCCL_HAS_CHAR8_T()
test_type<char8_t>();
#endif // _CCCL_HAS_CHAR8_T()
test_type<char16_t>();
test_type<char32_t>();
#if _CCCL_HAS_WCHAR_T()
test_type<wchar_t>();
#endif // _CCCL_HAS_WCHAR_T()

return true;
}

static_assert(test());

int main(int, char**)
{
NV_IF_TARGET(NV_IS_HOST, (test();))
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// <cuda/std/string_view>

// constexpr operator std::basic_string_view() const noexcept;

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

#include <string_view>

#include "literal.h"

template <class CharT>
constexpr void test_with_default_type_traits()
{
using HostSV = std::basic_string_view<CharT>;
using CudaSV = 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 HostSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename HostSV::traits_type, std::char_traits<CharT>>);
static_assert(cuda::std::is_same_v<typename CudaSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename CudaSV::traits_type, cuda::std::char_traits<CharT>>);

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

// test conversion to std::basic_string_view<CharT, std::char_traits<CharT>>
{
static_assert(cuda::std::is_convertible_v<CudaSV, HostSV>);
static_assert(noexcept(CudaSV{}.operator HostSV()));

CudaSV cuda_sv{str};
HostSV host_sv{cuda_sv};
assert(host_sv.data() == cuda_sv.data());
assert(host_sv.size() == cuda_sv.size());
}
}

template <class CharT>
struct custom_type_traits
: private std::char_traits<CharT>
, private cuda::std::char_traits<CharT>
{
using cuda::std::char_traits<CharT>::char_type;
using cuda::std::char_traits<CharT>::int_type;
using std::char_traits<CharT>::pos_type;
using std::char_traits<CharT>::off_type;
using std::char_traits<CharT>::state_type;

using cuda::std::char_traits<CharT>::assign;
using cuda::std::char_traits<CharT>::eq;
using cuda::std::char_traits<CharT>::lt;
using cuda::std::char_traits<CharT>::compare;
using cuda::std::char_traits<CharT>::length;
using cuda::std::char_traits<CharT>::find;
using cuda::std::char_traits<CharT>::move;
using cuda::std::char_traits<CharT>::copy;
using cuda::std::char_traits<CharT>::to_char_type;
using cuda::std::char_traits<CharT>::to_int_type;
using cuda::std::char_traits<CharT>::eq_int_type;
using std::char_traits<CharT>::eof;
using std::char_traits<CharT>::not_eof;
};

template <class CharT>
constexpr void test_with_custom_type_traits()
{
using HostSV = std::basic_string_view<CharT, custom_type_traits<CharT>>;
using CudaSV = 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 HostSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename HostSV::traits_type, custom_type_traits<CharT>>);
static_assert(cuda::std::is_same_v<typename CudaSV::value_type, CharT>);
static_assert(cuda::std::is_same_v<typename CudaSV::traits_type, custom_type_traits<CharT>>);

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

// test conversion to std::basic_string_view<CharT, custom_type_traits<CharT>>
{
static_assert(cuda::std::is_convertible_v<CudaSV, HostSV>);
static_assert(noexcept(CudaSV{}.operator HostSV()));

CudaSV cuda_sv{str};
HostSV host_sv{cuda_sv};
assert(host_sv.data() == cuda_sv.data());
assert(host_sv.size() == cuda_sv.size());
}
}

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

constexpr bool test()
{
test_type<char>();
#if _CCCL_HAS_CHAR8_T()
test_type<char8_t>();
#endif // _CCCL_HAS_CHAR8_T()
test_type<char16_t>();
test_type<char32_t>();
#if _CCCL_HAS_WCHAR_T()
test_type<wchar_t>();
#endif // _CCCL_HAS_WCHAR_T()

return true;
}

static_assert(test());

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