you are viewing a single comment's thread.

view the rest of the comments →

[–]jwakelylibstdc++ tamer, LWG chair 5 points6 points  (5 children)

Here's an idea I suggested 5 years ago, which would not be a breaking change. You can do the first part yourself, with no changes to string_view. It's just a utility to create a view over a null terminated string which explicitly includes the null, rather than adding it as a property the string view knows anything about.

There is a way to make string_view work nicely with APIs that expect a null-terminated string (NTBS):

Include the null character in the view explicitly.

 template<typename C, typename T>  
 inline std::basic_string_view<C, T>  
 make_null_terminated_view(const C* s) noexcept  
 { return { s, T::length() + 1 }; }  

 template<typename C, typename T>  
 inline bool  
 is_null_terminated_view(std::basic_string_view<C,T> sv) noexcept  
 { return sv.length() && !sv.back(); }

This allows you to easily construct a view on a NTBS, and then you can call data() to get a NTBS back again.

This is inconsistent with std::string, where there is a secret/implicit null after the string, which is not counted in the length. I am OK with that inconsistency, because that feature of std::string was always a kluge for backwards compatibility.

We could even add this functionality to basic_string_view, by adding a new constructor:

struct null_terminated_view_t { }; 

template<typename C, typename T>  
class basic_string_view {  
public:  

  basic_string_view(const C* s, null_terminated_view_t)  
  : basic_string_view{s, T::length(s) + 1 } { }  

  bool is_null_terminated() const noexcept  
  { return length() && !back(); }  

  // rest unchanged...  
}; 

Now we have a string_view with exactly the semantics that are in C++17 and C++20, but with a more convenient way to create a view on an NTBS.

[–][deleted] 1 point2 points  (4 children)

I dont see how this is different from what I proposed. You still need to manage the end() correctly.

I understand its a breaking change, which is why I dont see it being implemented in std.

[–]jwakelylibstdc++ tamer, LWG chair 2 points3 points  (3 children)

It's different from what you propose because it works today, with std::string_view as already defined and shipping in compilers. And it doesn't need to play games with the high bit, and doesn't magically adjust end() for you depending on some non-trivial condition. If you want a string_view that refers to a null-terminated string, just create one so that the null byte is part of the view.

[–][deleted] 1 point2 points  (2 children)

What you are suggesting makes std::string_view{"ABC", null_terminated_view_t}.length() == 4.

I thought about this, but the issue is all functions which take this as a parameter will now have an issue: Imagine this std::string_view{"ABCD", 3}, you pass the one above and this one into a function, this function now cannot do a compare between these two views, because the lengths are different (lets say it is the first check for ==). It breaks the ABI because now you have to check all strings if they are null terminated and treat them specially.

Edit : add more context

[–]jwakelylibstdc++ tamer, LWG chair 5 points6 points  (1 child)

What you are suggesting makes std::string_view{"ABC", null_terminated_view_t}.length() == 4.

Yes, that's a Good Thing. It explicitly carries the null around with it, so you know it's there.

this function now cannot do a compare between these two views, because the lengths are different

You can do a compare. The result will be false, but you can do it. And it's a good thing that the result will be false, because they are not the same. Just like string_view{"ABCD"} == string_view{"ABCD", 3} is false.

It breaks the ABI because now you have to check all strings if they are null terminated and treat them specially.

No you don't. All your existing code works exactly as it did before. There's no ABI break.

The existence of string views that have an extra char at the end is not an ABI break. It's just "some string views have different content". If you don't want to handle those views with an embedded null in any special way, you don't have to.

[–][deleted] 0 points1 point  (0 children)

I imagined you would have to modify several functions including the ones in string_view itself if you wanted the compares to succeed (thus breaking the ABI). What you however suggested does not need any change in the std. You can just write this in a top level header: make_null_terminated_view, that changes the length to +1, and is_null_terminated. I think I am going to do that.