We are going to implement a function that relies on some string_view features, and then, we see how many different types we can feed into it:
- The header includes and using directive come first:
#include <iostream>
#include <string_view>
using namespace std;
- We implement a function that accepts a string_view as its only argument:
void print(string_view v)
{
- Before doing anything with the input string, we remove any leading and trailing whitespace. We are not going to change the string, but the view on the string by narrowing it down to the actual non-whitespace part of the string. The find_first_not_of function will find the first character in the string, which is not space (' '), not a tab character ('t'), and not a newline character ('n'). With remove_prefix, we advance the internal string_view pointer to the first non-whitespace character. In case the string contains only whitespace, the find_first_not_of function returns the value npos, which is size_type(-1). As size_type is an unsigned variable, this boils down to a very large number. So, we take the smaller one of both: words_begin or the string view's size:
const auto words_begin (v.find_first_not_of(" tn"));
v.remove_prefix(min(words_begin, v.size()));
- We do the same with trailing whitespace. The remove_suffix shrinks down the view's size variable:
const auto words_end (v.find_last_not_of(" tn"));
if (words_end != string_view::npos) {
v.remove_suffix(v.size() - words_end - 1);
}
- Now we can print the string view and its length:
cout << "length: " << v.length()
<< " [" << v << "]n";
}
- In our main function, we play around with the new print function by feeding it with completely different argument types. First, we give it a runtime char* string from the argv pointer. At runtime, it contains the file name of our executable. Then, we give it an empty string_view instance. We then feed it with a C-style static character string, and with a ""sv literal, which constructs us a string_view on the fly. And finally, we give it an std::string. The nice thing is that none of these arguments are modified or copied in order to call the print function. No heap allocations happen. For many and/or large strings, this is very efficient:
int main(int argc, char *argv[])
{
print(argv[0]);
print({});
print("a const char * array");
print("an std::string_view literal"sv);
print("an std::string instance"s);
- We did not test the whitespace removal feature. So, let's give it a string that has a lot of leading and trailing whitespace:
print(" tn foobar n t ");
- Another cool feature is that the strings string_view gives us access to do not have to be zero-terminated. If we construct a string, such as "abc", without a trailing zero, the print function can still safely handle it because string_view also carries the size of the string it points to:
char cstr[] {'a', 'b', 'c'};
print(string_view(cstr, sizeof(cstr)));
}
- Compiling and running the program yields the following output. All the strings are correctly handled. The string we filled with lots of leading and trailing whitespace is correctly filtered, and the abc string without zero termination is also correctly printed without any buffer overflows:
$ ./string_view
length: 17 [./string_view]
length: 0 []
length: 20 [a const char * array]
length: 27 [an std::string_view literal]
length: 23 [an std::string instance]
length: 6 [foobar]
length: 3 [abc]