Is there a reason why string_view is not nullable?
This should not affect data() which returns a non-null pointer.
Is there a reason why string_view is not nullable?
Nullable here means default constructed or constructed from a nullptr(and a zero length). Then adding a is_null() (or null()) memberfunction which can then be used to distinguish the string_view from anempty string.
This should not affect data() which returns a non-null pointer.
On Dec 27, 2013, at 4:52 AM, abolz...@gmail.com wrote:
i think that attempting to distinguish between an empty string_view that “used to point at something”Is there a reason why string_view is not nullable?
Nullable here means default constructed or constructed from a nullptr(and a zero length). Then adding a is_null() (or null()) memberfunction which can then be used to distinguish the string_view from anempty string.and an empty string view “that never pointed at anything” is a use case to be discouraged, rather thansomething that should be enshrined in the standard.This should not affect data() which returns a non-null pointer.
I disagree here with the proposal as currently written.I believe that the requirement that data() always return a non-null pointer is misguided.
if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).
An empty string view need not point *anywhere*, or have any data behind it.— MarshallP.S. In my implementation, I’m tempted to have a default-constructed string_view return a value of 1 from data().
On Dec 27, 2013, at 4:52 AM, abolz...@gmail.com wrote:Is there a reason why string_view is not nullable?
Nullable here means default constructed or constructed from a nullptr(and a zero length). Then adding a is_null() (or null()) memberfunction which can then be used to distinguish the string_view from anempty string.i think that attempting to distinguish between an empty string_view that “used to point at something”and an empty string view “that never pointed at anything” is a use case to be discouraged, rather thansomething that should be enshrined in the standard.
This should not affect data() which returns a non-null pointer.
I disagree here with the proposal as currently written.I believe that the requirement that data() always return a non-null pointer is misguided.if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).
An empty string view need not point *anywhere*, or have any data behind it.— MarshallP.S. In my implementation, I’m tempted to have a default-constructed string_view return a value of 1 from data().
On Friday, December 27, 2013 11:01:34 PM UTC-6, Marshall wrote:if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).
My understanding (which I'd like confirmed) is that sv.data() is still useful as it provides a valid position within the referenced object, which might be referenced by other string_view instances or in its native form.
Thus, for example, a function object conforming to the delimiters interface documented at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3593.html#delimiters might return a zero-length string to denote a zero-length delimiter (e.g., the position following a period in an English sentence). In this case it is the value of sv.data() that tells the caller where the delimited sequence ends and the next sequence begins.
On Friday, December 27, 2013 11:01:34 PM UTC-6, Marshall wrote:On Dec 27, 2013, at 4:52 AM, abolz...@gmail.com wrote:Is there a reason why string_view is not nullable?
Nullable here means default constructed or constructed from a nullptr(and a zero length). Then adding a is_null() (or null()) memberfunction which can then be used to distinguish the string_view from anempty string.i think that attempting to distinguish between an empty string_view that “used to point at something”and an empty string view “that never pointed at anything” is a use case to be discouraged, rather thansomething that should be enshrined in the standard.
-1 ; I think it's an obvious and valuable feature.This should not affect data() which returns a non-null pointer.
I disagree here with the proposal as currently written.I believe that the requirement that data() always return a non-null pointer is misguided.if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).
My understanding (which I'd like confirmed) is that sv.data() is still useful as it provides a valid position within the referenced object, which might be referenced by other string_view instances or in its native form.
a string view refers to a half open range (just like an iterator pair).On Dec 28, 2013, at 11:21 AM, Peter Bigot <big...@acm.org> wrote:
On Friday, December 27, 2013 11:01:34 PM UTC-6, Marshall wrote:On Dec 27, 2013, at 4:52 AM, abolz...@gmail.com wrote:This should not affect data() which returns a non-null pointer.
I disagree here with the proposal as currently written.I believe that the requirement that data() always return a non-null pointer is misguided.if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).
My understanding (which I'd like confirmed) is that sv.data() is still useful as it provides a valid position within the referenced object, which might be referenced by other string_view instances or in its native form.
sv.data() a pointer to the beginning of that range, just like sv.begin() is an iterator to the beginning of that range.
If sv.size() == 0, then dereferencing sv.data() or sv.begin() is undefined behavior.
Also, I can’t find anywhere in the specification does it guarantee that the following code works:const char *foo = “abc”;string_view sv1 ( foo, 3 ); // size() == 3
sv1. remove_suffix (3); // size() == 0
assert ( sv1.size() == 0);assert ( sv1.data() == foo ); // will probably work - but might not.
Hopefully the new version at
https://github.com/google/cxx-std-draft/blob/string-ref-paper/string_view.html,
which describes things in terms of exposition-only data_ and size_
members, makes this clearer.
i think that attempting to distinguish between an empty string_view that “used to point at something”and an empty string view “that never pointed at anything” is a use case to be discouraged, rather thansomething that should be enshrined in the standard.This should not affect data() which returns a non-null pointer.
I disagree here with the proposal as currently written.I believe that the requirement that data() always return a non-null pointer is misguided.if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).The thing is, if nullptr is a possible return value for data(), people do start using it to convey information. If they get a null out exactly when they put a null in, it is telling them something useful. The only way to "discourage" it is to make it never happen. If we let string_view::data() pass through null, we're enshrining the use you don't like in the standard.
if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).
The null option comes in handy when you need to distinguish a field that might
be present but empty from a field that isn't present. QUrl makes use of that
and the equivalent std::networking::uri proposal just uses
std::optional<String> (String is a template).
While the half-open range argument is true in isolation, it's missing the point that the value of data() is well-known based on how the string_view was created and what operations have been performed on it.
Adjusting the string_view to the point where size() is zero does not invalidate data().
Based on how sv2 is constructed in that example, I believe it's guaranteed that deref(sv2) will return the '\0' that terminates the string literal used to initialize sv2, and that doing so does not invoke undefined behavior.
remove the precondition on the string_view(data, size) to let peopleIf it is, I intend to also
explicitly put nullptr in.
On 17 January 2014 12:34, Peter Bigot <big...@acm.org> wrote:
While the half-open range argument is true in isolation, it's missing the point that the value of data() is well-known based on how the string_view was created and what operations have been performed on it.How does the callee know how the string_view was created? If the callee has more preconditions than those enforced by string_view, you might be better off using a separate type. If the callee "knows" how it was created, why use a string_view at all?
Based on how sv2 is constructed in that example, I believe it's guaranteed that deref(sv2) will return the '\0' that terminates the string literal used to initialize sv2, and that doing so does not invoke undefined behavior.What compilers are allowed to assume from the specification of the standard library is an interesting question... :-)Take for example:std::vector<std::string> v(1);
auto p1 = v.data();v.pop_back();Can I legally do a placement new of a std::string into the space pointed to by p1? Beats me.
On Friday, January 17, 2014 10:26:50 AM UTC-6, Marshall wrote:On Jan 15, 2014, at 4:28 AM, Olaf van der Spek <olafv...@gmail.com> wrote:On Saturday, December 28, 2013 6:01:34 AM UTC+1, Marshall wrote:if sv.size () == 0 or sv.empty () [same thing], there is nothing that you can do with sv.data()(except compare it to null, and that doesn’t tell you anything useful).Are you sure?IMO an empty string_view could still point into a string and I should be able to use that pointer to for example construct a new string_view.A string view references a half-open contiguous range of characters; [ data(), data()+size())if size() == 0, then the range of characters referenced by the string view is empty, and accessing them (either via dereferencing begin() or data()) is (or should be) undefined behavior. (the same as any other container)If in your program, you “know” that the pointer returned by data() points to something valid, well, that’s on you to determine.Code example:char deref ( string_view sv ) { return *sv.data(); }string_view sv1;string_view sv2 { “ABCDE” };sv2.remove_prefix(5);assert ( sv1 == sv2 );deref ( sv1 ); // may or may not blow up in your facederef ( sv2 ); // this will probably “work” ; but I don’t know what it will return.— Marshall
While the half-open range argument is true in isolation, it's missing the point that the value of data() is well-known based on how the string_view was created and what operations have been performed on it. Adjusting the string_view to the point where size() is zero does not invalidate data().
On Fri, Jan 17, 2014 at 2:22 PM, Jeffrey Yasskin <jyas...@google.com> wrote:
On Fri, Jan 17, 2014 at 12:18 PM, Nevin Liber <ne...@eviloverlord.com> wrote:
> On 17 January 2014 14:11, Jeffrey Yasskin <jyas...@google.com> wrote:
>>
>> If it is, I intend to also
>> remove the precondition on the string_view(data, size) to let people
>> explicitly put nullptr in.
>
>
> I'd like to, mainly because it makes it easy to create a string_view from a
> vector<char> by using v.data() and v.size(). Having to special case a check
> for nullptr (such as in string::string(const char*, size_t)) for what is
> otherwise a perfectly valid 0-sized range is just annoying.Ah, vector.data() doesn't guarantee non-null results. :( That's a good argument.
First, is that true? [data(), data()+size()) must be a valid range, but I've been unable to find a definition of "valid range" in the standard so I could understand whether an empty valid range permits its base to be a null pointer.
Second, even if so std::string.data() does guarantee a non-null result, doesn't it? In that case it's also required that data()+i == &operator[](i) for i in [0, size()] which led me to believe data() could not be null, since the equality must hold for i==0 and operator[] must return a reference to an object (hence the underlying pointer cannot be null).
I've been swayed by the argument that there's no such thing as a null std::string, so there should be no such thing as a null std::string_view. This is easily assured by disallowing std::string_view(s, n) for a null s just as std::string(s, n) is disallowed for null s.
I don't see it as unreasonable to require a check for that situation before creating a std::string_view in situations where a char* pointer comes from a source that might produce a null pointer. As Nevin points out, you have to do it for std::string too.
Personally, I'd find it more annoying to have to constantly check in every utility function that operates on string_view instances whether an input sv.data() is null in addition to whether sv.size() is zero.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
On Jan 17, 2014, at 1:25 PM, Peter Bigot <big...@acm.org> wrote:On Fri, Jan 17, 2014 at 2:22 PM, Jeffrey Yasskin <jyas...@google.com> wrote:
On Fri, Jan 17, 2014 at 12:18 PM, Nevin Liber <ne...@eviloverlord.com> wrote:
> On 17 January 2014 14:11, Jeffrey Yasskin <jyas...@google.com> wrote:
>>
>> If it is, I intend to also
>> remove the precondition on the string_view(data, size) to let people
>> explicitly put nullptr in.
>
>
> I'd like to, mainly because it makes it easy to create a string_view from a
> vector<char> by using v.data() and v.size(). Having to special case a check
> for nullptr (such as in string::string(const char*, size_t)) for what is
> otherwise a perfectly valid 0-sized range is just annoying.Ah, vector.data() doesn't guarantee non-null results. :( That's a good argument.
First, is that true? [data(), data()+size()) must be a valid range, but I've been unable to find a definition of "valid range" in the standard so I could understand whether an empty valid range permits its base to be a null pointer.
Second, even if so std::string.data() does guarantee a non-null result, doesn't it? In that case it's also required that data()+i == &operator[](i) for i in [0, size()] which led me to believe data() could not be null, since the equality must hold for i==0 and operator[] must return a reference to an object (hence the underlying pointer cannot be null).I think you mean, [0,i), not [0,i]
I've been swayed by the argument that there's no such thing as a null std::string, so there should be no such thing as a null std::string_view. This is easily assured by disallowing std::string_view(s, n) for a null s just as std::string(s, n) is disallowed for null s.The difference here is that string manages its own storage, while string_view does not.
Agreed. To mitigate this, if string_view::data() could return nullptr, I believe we could still create strings using std::string(null_sv.begin(), null_sv.end()).
I have not followed this entire thread, instead I hacked up a little implementation which I used in implementing something for another thread on an improved "printf" functionality. What I noticed was:- I had use for a C array of string_view to keep the parts of the original string that fell between each data inserted. This means that I must be able to default construct string_view as there is no other way to construct an array element.
- It is logical to do this default construction by setting m_begin and m_end to nullptr, or m_begin to nullptr and m_size to 0.
- It must be allowed to take a default constructed string_view as the range of a range based for statement
__or__ to check if the range is in default constucted state using something like os_null().
- both of these methods are impossible if the presumptions stated above "begin() should never return nullptr" and "we don't need a special is_null()". Well, not entirely. Of course you could create some bogus object and use its address instead of nullptr.Someone stated above that the \0 at the end of the string should be accessible even if the string_view is empty. This is clearly impossible as a string_view may be constructed as a part of a string which continues on. Thus there is no \0 at *end() anytime.
I did not propose using unspecialized optional. I suggested to add specialization of optional<basic_string_view<...>> to string_view header. Thus any use of optional<string_view> will pick up specialized version that can have constructors that you desire.
Ok. But an unspecialized optional<string_view> is very different from a nullable string_view.And a specialized optional<string_view> would be very different from an optional<T>.
Ok. But an unspecialized optional<string_view> is very different from a nullable string_view.And a specialized optional<string_view> would be very different from an optional<T>.
I need to make sure that the specialized version is constructible from a nullptr andand then constructing a disengaged optional.
What should the relational operators of this specialization look like? I would like to beable to write something like
void f(optional<string_view> s){if (s == "abc") {}if (s < "abc") {}}
I don't see why there should be a difference between specialized and unspecialized optional<string_view>. I also don't see how it is different from a "null string_view". As far as I can see the semantics are equivalent. As long as string_view does not accept nullptr the specialization is nothing but a sizeof() optimizing implementation detail.
On 19 January 2014 15:08, Miro Knejp <mi...@knejp.de> wrote:
I don't see why there should be a difference between specialized and unspecialized optional<string_view>. I also don't see how it is different from a "null string_view". As far as I can see the semantics are equivalent. As long as string_view does not accept nullptr the specialization is nothing but a sizeof() optimizing implementation detail.
In which case it is just a Quality of Implementation issue and need not be addressed by the Standard.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
What is the difference between a "nullable string_view" and an "optional<string_view>"? Not in terms of syntax but semantics. That's what must be the same. Both give you means of marking the string_view as *null* and both cause errors/UB if accessing such a string.
Ok. But an unspecialized optional<string_view> is very different from a nullable string_view.And a specialized optional<string_view> would be very different from an optional<T>.
If done properly the only difference between optional<T> and optional<string_view> is sizeof() by optimizing away a bool member and use .data() == nullptr for determining whether the optional is engaged or not. As long as string_view does not accept null this distinction can be made.
Do the null check before constructing the optional. If the string_view constructor does not accept nullptr neither does optional<string_view>(in_place_t, ...). Just like for any other T.
I need to make sure that the specialized version is constructible from a nullptr andand then constructing a disengaged optional.
Since it's an optional<T> you still have to use it like an optional<T>. If "s" is disengaged both comparsions return false, which is correct and I would expect the same for a null string_view. Just like with any other T.
What should the relational operators of this specialization look like? I would like to beable to write something like
void f(optional<string_view> s){if (s == "abc") {}if (s < "abc") {}}
Instead of using "if(s.data())" use "if(s)" or "if(!s)" to check for null.
I don't see why there should be a difference between specialized and unspecialized optional<string_view>. I also don't see how it is different from a "null string_view". As far as I can see the semantics are equivalent. As long as string_view does not accept nullptr the specialization is nothing but a sizeof() optimizing implementation detail.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
I just don't want to do that. I have a function like
void f(char const* s) {if (s == nullptr)dothis();elsedothat();}
and I want to replace char const* with string_view to make it accept a std::string so I can save some std::string.c_str()calls.
restricts the cases where a string_view would be useful.
constexpr basic_string_view(const charT* str, size_type len);
Requires: str is not a null pointer and [str,str + len) is a valid range.
with
constexpr basic_string_view(const charT* str, size_type len);
Requires: str is not a null pointer and [str,str + len) is a valid range
or str == nullptr and len == 0.
address your concerns?
On further reflection it'd better to store a null pointer in m_ptr to represent a default-constructed value but to return &m_nul from data() when m_ptr is null.
str
is not a null pointer and [str
,str + len
) is a valid range.
For a function like
void print(string const& s) {}
there is an implicit precondition: a string can not be constructed from a nullptr.So you have to check this before calling. When replacing string const& with string_view,you just need to make the precondition explicit. Then in both cases data() != null.
If you use string.data(), you can't simply replace func(string const&) with func(string_view)even if data() returns non-null, since data() might not be null terminated.
void print(string const& s) {printf("%s", s.data());}
If yyou don't use data() then, well... its not important if data() returns nullptr or not.
and "have an optimized optional<string_view>" proposals
as both allow null's for other purposes.
Yes, I as author of print have the implicit precondition enforced by std::string that it's always called with a valid string. That is something I can rely on. That is a good thing. Changing the signature to accept (nullable) string_view suddenly breaks this guarantee and I either have to add asserts or some other kinds of checks and document it somewhere and needlessly complexify every method relying on this guarantee. That is a bad thing. And suddenly you need to create new test cases for print even though it was working perfectly. That is also a bad thing.
For a function like
void print(string const& s) {}
there is an implicit precondition: a string can not be constructed from a nullptr.So you have to check this before calling. When replacing string const& with string_view,you just need to make the precondition explicit. Then in both cases data() != null.
If one used .data() to pass it to (const char*) methods (which should ring a bell and make you pay *very good attention* to what you're doing) before C++11 then it was already calling for disaster as .data() was not guaranteed to be null terminated. Then there are the methods which take a pointer and a length. There it doesn't matter but they might crash if passed in null (even if the length is 0). Seen it happen more often than I like. I always tell people to use .c_str() because it is more explicitly stating intentions and it is backwards compatible.
If you use string.data(), you can't simply replace func(string const&) with func(string_view)even if data() returns non-null, since data() might not be null terminated.
void print(string const& s) {printf("%s", s.data());}
If yyou don't use data() then, well... its not important if data() returns nullptr or not.
null always makes things more complicated than necessary. Just notice how the library is evolving to gradually remove the word and value "null" from our everyday usage. That's a good thing. Think in abstractions, not null. Throwing null at everyone only to enable a few edge cases seems wrong. One can always come up with situations where a nullable string_view might be useful, as is the case with many other things deemed "bad", but in the end it matters how common that scenario is compared to where a non-null guarantee makes code safer, simpler and more robust.
I do think that string_view is more a reference type than a collection type (thought it is both), so there may be an argument in support of being able to detect whether an instance holds a valid reference (i.e. does not have a default-constructed value, including a value equivalent to default-construction due to invocation of clear()). If this is desirable, it can be detected easily by adding a member function has_reference() that returns !!m_ptr.
Why would ( nullptr, 3 ) be considered an empty view?
--
> My personal preference for all this is that string_view(nullptr, 0) beThat's interesting. It's easier to both specify and implement to just
> allowed, and it is a Quality of Implementation issue whether or not
> sv.data() can ever return a nullptr; i.e., it would be allowed to return any
> legal pointer it wants for an empty string_view.
say that string_view(x, y).data()==x and string_view(x, y).size()==y,
assuming [x, x+y) is a valid range. Why would you special-case data()
to return arbitrary things when y==0?
I would also argue against string_view( char* ) doing runtime null checks and creating an empty view, as this only defends against ( P == null ) and not P being in protected memory, of which null falls into.