Skip to content

Commit

Permalink
Add boost/core/string_view.hpp
Browse files Browse the repository at this point in the history
  • Loading branch information
pdimov committed Oct 4, 2021
1 parent 5e382ef commit 95924b1
Show file tree
Hide file tree
Showing 4 changed files with 799 additions and 0 deletions.
181 changes: 181 additions & 0 deletions doc/string_view.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
[/
Copyright 2021 Peter Dimov
Distributed under the Boost Software License, Version 1.0.
https://boost.org/LICENSE_1_0.txt
]

[section:string_view string_view]

[simplesect Authors]

* Peter Dimov

[endsimplesect]

[section Header <boost/core/string_view.hpp>]

The header `<boost/core/string_view.hpp>` defines `boost::core::string_view`,
a portable and interoperable implementation of `std::string_view`.

Unlike `boost::string_view`, `boost::core::string_view` has implicit
conversions from and to `std::string_view`, which allows Boost libraries that
support C++11/C++14 to use it in interfaces without forcing users to forgo the
use of `std::string_view` in their code.

[section Synopsis]

``
namespace boost
{
namespace core
{

template<class Ch> class basic_string_view
{
private:

Ch const* data_;
std::size_t size_;

public:

// types

typedef std::char_traits<Ch> traits_type;
typedef Ch value_type;
typedef Ch* pointer;
typedef Ch const* const_pointer;
typedef Ch& reference;
typedef Ch const& const_reference;
typedef Ch const* const_iterator;
typedef const_iterator iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef const_reverse_iterator reverse_iterator;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;

// npos

static constexpr size_type npos = static_cast<size_type>( -1 );

public:

// construction and assignment

constexpr basic_string_view() noexcept;
constexpr basic_string_view( basic_string_view const& ) noexcept = default;
constexpr basic_string_view& operator=( basic_string_view const& ) noexcept & = default;
constexpr basic_string_view( Ch const* str ) noexcept;
constexpr basic_string_view( Ch const* str, size_type len ) noexcept;
constexpr basic_string_view( Ch const* begin, Ch const* end ) noexcept;
template<class A> basic_string_view(std::basic_string<Ch, std::char_traits<Ch>, A> const& str ) noexcept;
basic_string_view(std::basic_string_view<Ch, std::char_traits<Ch>> const& str ) noexcept;

// iterator support

constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr const_iterator cbegin() const noexcept;
constexpr const_iterator cend() const noexcept;
constexpr const_reverse_iterator rbegin() const noexcept;
constexpr const_reverse_iterator rend() const noexcept;
constexpr const_reverse_iterator crbegin() const noexcept;
constexpr const_reverse_iterator crend() const noexcept;

// capacity

constexpr size_type size() const noexcept;
constexpr size_type length() const noexcept;
constexpr size_type max_size() const noexcept;
constexpr bool empty() const noexcept;

// element access

constexpr const_reference operator[]( size_type pos ) const noexcept;
constexpr const_reference at( size_type pos ) const;
constexpr const_reference front() const noexcept;
constexpr const_reference back() const noexcept;
constexpr const_pointer data() const noexcept;

// modifiers

constexpr void remove_prefix( size_type n ) noexcept;
constexpr void remove_suffix( size_type n ) noexcept;
constexpr void swap( basic_string_view& s ) noexcept;

// string operations

constexpr size_type copy( Ch* s, size_type n, size_type pos = 0 ) const;
constexpr basic_string_view substr( size_type pos = 0, size_type n = npos ) const;

constexpr int compare( basic_string_view s ) const noexcept;
constexpr int compare( size_type pos1, size_type n1, basic_string_view s ) const;
constexpr int compare( size_type pos1, size_type n1, basic_string_view s, size_type pos2, size_type n2 ) const;
constexpr int compare( Ch const* s ) const;
constexpr int compare( size_type pos1, size_type n1, Ch const* s ) const;
constexpr int compare( size_type pos1, size_type n1, Ch const* s, size_type n2 ) const;

constexpr bool starts_with( basic_string_view x ) const noexcept;
constexpr bool starts_with( Ch x ) const noexcept;
constexpr bool starts_with( Ch const* x ) const;

constexpr bool ends_with( basic_string_view x ) const noexcept;
constexpr bool ends_with( Ch x ) const noexcept; constexpr bool ends_with( Ch const* x ) const;

// searching

constexpr size_type find( basic_string_view s, size_type pos = 0 ) const noexcept;
constexpr size_type find( Ch c, size_type pos = 0 ) const noexcept;
constexpr size_type find( Ch const* s, size_type pos, size_type n ) const;
constexpr size_type find( Ch const* s, size_type pos = 0 ) const;

constexpr size_type rfind( basic_string_view s, size_type pos = npos ) const noexcept;
constexpr size_type rfind( Ch c, size_type pos = npos ) const noexcept;
constexpr size_type rfind( Ch const* s, size_type pos, size_type n ) const;
constexpr size_type rfind( Ch const* s, size_type pos = npos ) const;

constexpr size_type find_first_of( basic_string_view s, size_type pos = 0 ) const noexcept;
constexpr size_type find_first_of( Ch c, size_type pos = 0 ) const noexcept;
constexpr size_type find_first_of( Ch const* s, size_type pos, size_type n ) const;
constexpr size_type find_first_of( Ch const* s, size_type pos = 0 ) const;

constexpr size_type find_last_of( basic_string_view s, size_type pos = npos ) const noexcept;
constexpr size_type find_last_of( Ch c, size_type pos = npos ) const noexcept;
constexpr size_type find_last_of( Ch const* s, size_type pos, size_type n ) const;
constexpr size_type find_last_of( Ch const* s, size_type pos = npos ) const;

constexpr size_type find_first_not_of( basic_string_view s, size_type pos = 0 ) const noexcept;
constexpr size_type find_first_not_of( Ch c, size_type pos = 0 ) const noexcept;
constexpr size_type find_first_not_of( Ch const* s, size_type pos, size_type n ) const;
constexpr size_type find_first_not_of( Ch const* s, size_type pos = 0 ) const;

constexpr size_type find_last_not_of( basic_string_view s, size_type pos = npos ) const noexcept;
constexpr size_type find_last_not_of( Ch c, size_type pos = npos ) const noexcept;
constexpr size_type find_last_not_of( Ch const* s, size_type pos, size_type n ) const;
constexpr size_type find_last_not_of( Ch const* s, size_type pos = npos ) const;};

typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
typedef basic_string_view<char16_t> u16string_view;
typedef basic_string_view<char32_t> u32string_view;
typedef basic_string_view<char8_t> u8string_view;

} // namespace core
} // namespace boost
``

[endsect]

[section Construction]

[section constexpr basic_string_view() noexcept;]

* *Ensures:* `data() == 0`, `size() == 0`.

[endsect]

[endsect]

[endsect]

[endsect]
Expand Down
Loading

13 comments on commit 95924b1

@Lastique
Copy link
Member

@Lastique Lastique commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

I have to say, I'm not a fan of this. Now we have two different string_view types, not counting std::string_view and boost::string_ref, when it is supposed to be a universal vocabulary type. :( How is this supposed to coexist with boost::string_view from Boost.Utility? Have you discussed this with @mclow?

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

Marshall refuses to add conversions from and to std::string_view to "his" string_view. People have been asking him that for probably a year now, if not more. Libraries need an interoperable string_view that they can use without requiring C++17 but which can still accept and convert to std::string_view.

E.g. if you have the API

string_view my_function( string_view str );

people who use C++17 want to be able to invoke my_function with a std::string_view and then store the result in a std::string_view. With this string_view, they can. With boost::string_view, they cannot.

@Lastique
Copy link
Member

Choose a reason for hiding this comment

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

I'm aware of that situation, and I also think the conversions would be useful. But that doesn't look like a reason enough to introduce yet another string_view. Because now people will need to have this:

// And what should these functions return now?
boost::string_view my_function( boost::string_view str );
boost::core::string_view my_function( boost::core::string_view str );

And yes, full disclosure, I'm going to be affected by this in Boost.Log, and I'm not happy about this.

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

I don't understand the question above; why would you want to have these?

@Lastique
Copy link
Member

Choose a reason for hiding this comment

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

Because supposedly the one who writes this API wants to accept any string in my_function. If he accepts Boost string_views as part of this, it is only logical to accept both now.

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

If he accepts Boost string_view, then he doesn't accept std::string_view, which means he doesn't accept any string. That's exactly the problem with boost::string_view that we have and need to solve.

I understand that in Boost.Log you'll now need to add more overloads, but I don't think denying everyone else a converting string_view is the best solution to that problem. I would instead look into defining an is_string metafunction. Ironically, the best implementation that comes to mind - checking that T::traits_type is std::char_traits<T> - disallows arbitrary traits, but there are other options, e.g. checking that T::traits_type::char_type is T::value_type. :-) (You'd also need to verify that data() and size() exist and return T const* and size_type.)

@Lastique
Copy link
Member

Choose a reason for hiding this comment

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

I'm not arguing because I have to modify Boost.Log, I'm arguing because that change does not make things better - it makes code more complicated and more heavy. And this kind of changes will not be localized to Boost.Log or Boost. Whereas the point of string_view is exactly the opposite.

If he accepts Boost string_view, then he doesn't accept std::string_view, which means he doesn't accept any string.

He does, though it requires explicit conversion. Yes, a nuicance, but not a killer, and definitely not as much of a problem as adding yet another overload. Which is incompatible with boost::string_view and std::string_view anyway because of the different template parameters. This is actually a problem for users in Boost as they won't be able to use boost::core::string_view as a drop-in replacement for std::string_view, which is often done to lower C++ version requirements.

What I think should be done, is further discussion with @mclow and on the ML, possibly with a vote to force the change in boost::string_view as widely requested. I don't think Marshall explained his motivation for his decision, and I suspect he might not have understood what was being proposed (there was a lot of confusion about Boost.Config macros in that PR).

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

A new library that only accepts and returns boost::core::string_view doesn't have this problem. It accepts std::string and std::string_view automatically, and it "returns" std::string_view automatically. No overloads are required.

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

I suppose an argument needs to be made here that boost::core::string_view should convert from and to boost::string_view as well, but doing that without including its header is hard without some form of cooperation from boost::string_view. (It can't be forward-declared.)

@Lastique
Copy link
Member

Choose a reason for hiding this comment

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

A new library that only accepts and returns boost::core::string_view doesn't have this problem.

There is arguably a lot of code written for boost::string_view, that code won't work with that interface. And some code involving template specializations and templated overloads like in Boost.Log won't ever work with boost::core::string_view until modified. You can't solve every problem by adding conversions, and multiple string view types is still a problem.

BTW, I think you can forward-declare as long as default template arguments match.

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

I see no better path forward. It's not like I was particularly thrilled by the thought of writing another string_view, complete with a test suite.

Either way, this discussion belongs on the list. I don't mind throwing this entire thing in the trash if needed, as long as a better solution to the problem is offered and implemented.

BTW, I think you can forward-declare as long as default template arguments match.

Compilers disagree. https://godbolt.org/z/EeYoYTG38

@Lastique
Copy link
Member

Choose a reason for hiding this comment

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

I posted a message on the ML.

BTW, I think you can forward-declare as long as default template arguments match.

Compilers disagree. https://godbolt.org/z/EeYoYTG38

Ok, then you can omit the default: https://godbolt.org/z/bb35xEWGT

@pdimov
Copy link
Member Author

@pdimov pdimov commented on 95924b1 Oct 16, 2021

Choose a reason for hiding this comment

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

Yes, this might work.

Please sign in to comment.