25

I have a method that takes std::string_view and uses function, which takes null terminated string as parameter. For example:

void stringFunc(std::experimental::string_view str) {
    some_c_library_func(/* Expects null terminated string */);
}

The question is, what is the proper way to handle this situation? Is str.to_string().c_str() the only option? And I really want to use std::string_view in this method, because I pass different types of strings in it.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Schtolc
  • 1,036
  • 1
  • 12
  • 19

4 Answers4

28

I solved this problem by creating an alternate string_view class called zstring_view. It's privately inherited from string_view and contains much of its interface.

The principal difference is that zstring_view cannot be created from a string_view. Also, any string_view APIs that would remove elements from the end are not part of the interface or they return a string_view instead of a zstring_view.

They can be created from any NUL-terminated string source: std::string and so forth. I even created special user-defined literal suffixes for them: _zsv.

The idea being that, so long as you don't put a non-NUL-terminated string into zstring_view manually, all zstring_views should be NUL-terminated. Like std::string, the NUL character is not part of the size of the string, but it is there.

I find it very useful for dealing with C interfacing.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    I just did the same, and I agree the interface is very nice and removes the last reason for ever doing const std::string& as a parameter. You do lose a lot of convenience using the private inheritance (like comparison, and conversion back to string_view) - looking at the interface, it seems the only problematic mutators are remove_***fix and swap - I just reimplemented swap, and added remove_***fix as private non-implemented members, and everything seems to work fine - so you can indeed publicly inherit from std::string_view. Only issue is new mutators added to string_view in the future. – Shaggi Nov 26 '17 at 03:50
  • 1
    Although it could be said it is bad style. And additionally, you would have issues with passing zstring_views to functions taking string_view& (which is weird in more than two ways...) – Shaggi Nov 26 '17 at 04:12
  • 1
    @Shaggi `zstring_view` is pretty safe to allow to decay to `string_view` implicitly, for convenience. It's the reverse order that needs to be guarded against. – VoidStar Jan 27 '21 at 09:12
  • Agree, but if you're deriving publicly, no conversion happens when passing a zstring_view to a function taking string_view&, making it unsafe. – Shaggi Jan 31 '21 at 15:26
11

You cannot alter a string through std::string_view. Therefore you cannot add a terminating '\0' character. Hence you need to copy the string somewhere else to add a '\0'-terminator. You could avoid heap allocations by putting the string on the stack, if it's short enough. If you know, that the std::string_view is part of a null-terminated string, then you may check, if the character past the end is a '\0' character and avoid the copy in that case. Other than that, I don't see much more room for optimizations.

Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120
5

You certainly shouldn't call data on std::experimental::string_view:

Unlike basic_string::data() and string literals, data() may return a pointer to a buffer that is not null-terminated.

So call to_string and c_str on that:

void stringFunc(std::experimental::string_view str) {
    some_c_library_func(str.to_string().c_str());
}

or:

void stringFunc(std::experimental::string_view str) {
    std::string real_str(str);
    some_c_library_func(real_str.c_str());
}
Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • 1
    string::data() returns a pointer to just the raw character string, which is NOT necessarily null-terminated. If you want a null-terminated string, use string::c_str() – Howard Lee Harkness Mar 21 '18 at 17:45
  • 4
    @HowardLeeHarkness since C++11, it *is* necessarily null-terminated. See e.g. https://www.cplusplus.com/reference/string/string/data/ – minexew Jan 15 '20 at 22:00
0

In some cases C-stype functions have overloads, which accept the length of string as a separate argument.

E.g. instead of using strcasesmp() it worth to switch to strncasecmp(). For sure, in this particular case, it would require additional logic implemented in case when strings are not equal, but the first n characters are equal.

But it could be good alternative to writing custom class for string views.

Alexander Stepaniuk
  • 6,217
  • 4
  • 31
  • 48