Guidelines Support Library Review: string_span<T>

In a previous post I have introduced the span<T> type from the Guidelines Support Library. This is a non-owning range of contiguous memory recommended to be used instead of pointers (and size counter) or standard containers (such as vector or array). span<T> can be used with strings, but the Guidelines Support Library provides a different span implementation for various types of strings. These string span types are available in the string_span.h header.

String span types

There are several string span types defined in the string_span.h header:

  • basic_string_span: the actual implementation for a string span on which several type aliases are available:
    • string_span: a string span of char
    • cstring_span: a string span of const char
    • wstring_span: a string span of wchar_t
    • cwstring_span: a string span of const wchar_t
    template<std::ptrdiff_t Extent = dynamic_range>
    using string_span = basic_string_span<char, Extent>;
    
    template<std::ptrdiff_t Extent = dynamic_range>
    using cstring_span = basic_string_span<const char, Extent>;
    
    template<std::ptrdiff_t Extent = dynamic_range>
    using wstring_span = basic_string_span<wchar_t, Extent>;
    
    template<std::ptrdiff_t Extent = dynamic_range>
    using cwstring_span = basic_string_span<const wchar_t, Extent>;
  • basic_zstring_span: a null terminated string span used for converting null terminated spans to legacy strings; it has several type aliases available:
    • zstring_span: a null terminated string span of char
    • czstring_span: a null terminated string span of const char
    • wzstring_span: a null terminated string span of wchar_t
    • cwzstring_span: a null terminated string span of const wchar_t
    template <std::ptrdiff_t Max = dynamic_range>
    using zstring_span = basic_zstring_span<char, Max>;
    
    template <std::ptrdiff_t Max = dynamic_range>
    using wzstring_span = basic_zstring_span<wchar_t, Max>;
    
    template <std::ptrdiff_t Max = dynamic_range>
    using czstring_span = basic_zstring_span<const char, Max>;
    
    template <std::ptrdiff_t Max = dynamic_range>
    using cwzstring_span = basic_zstring_span<const wchar_t, Max>;

These look like a lot of classes with similar names, but the names are self explanatory (terminology is c=const, w=wide, z=null-terminated):

  • string: a string of char
  • cstring: a string of const char
  • wstring: a string of wchar_t
  • cwstring: a string of const wchar_t
  • zstring: a (zero) null-terminated string of char
  • czstring: a null-terminated string of const char
  • wzstring: a null-terminated string of wchar_t
  • cwzstring: a null-terminated string of const wchar_t

Creating a string_span

A string_span can be created in many ways, including:

(Note that in all following examples the string span is the range { L'H',L'e',L'l',L'l',L'o',L' ',L'w',L'o',L'r',L'l',L'd' } of either char or wchar_t.)

  • from a literal string
    gsl::cstring_span<> s1 = gsl::ensure_z("Hello world");
    
    gsl::cwstring_span<> s2 = gsl::ensure_z(L"Hello world");
  • from a pointer
    const char* t1 = "Hello world";
    gsl::cstring_span<> s1 = gsl::ensure_z(t1);
    
    const wchar_t* t2 = L"Hello world";
    gsl::cwstring_span<> s2 = gsl::ensure_z(t2);
  • from a standard string
    std::string str1 = "Hello world";
    gsl::cstring_span<> s1 = str1;
    
    std::wstring wstr1 = L"Hello world";
    gsl::cwstring_span<> s2 = wstr1;
  • from an array
    char a1[] = "Hello world";
    gsl::cstring_span<> s1 = gsl::ensure_z(a1);
    
    wchar_t a2[] = L"Hello world";
    gsl::cwstring_span<> s2 = gsl::ensure_z(a2);
  • from a vector
    std::vector<char> vec1 = { 'H','e','l','l','o',' ','w','o','r','l','d' };
    gsl::string_span<> s1 = vec1;
    
    std::vector<wchar_t> vec2 = { L'H',L'e',L'l',L'l',L'o',L' ',L'w',L'o',L'r',L'l',L'd' };
    gsl::wstring_span<> s2 = vec2;

Converting to string

To convert a string span into a string you can use the to_string() function.

char a1[] = "Hello world";
gsl::cstring_span<> s1 = gsl::ensure_z(a1);
std::string str1 = gsl::to_string(s1);   // str1 = "Hello world"

wchar_t a2[] = L"Hello world";
gsl::cwstring_span<> s2 = gsl::ensure_z(a2);
std::wstring str2 = gsl::to_string(s2);  // str2 = L"Hello world"

Size of a string_span

Unlike span<T>, a string_span<T> only have one dimension, so the rank() method does not make make sense and is not available. However, a string span has several methods for the size of the span:

  • size() and length(): return the number of elements of the span
  • size_bytes() and length_bytes(): return the number of bytes of the span
std::string str1 = "Hello world";
gsl::cstring_span<> s1 = str1;

std::cout << "length=" << s1.length() << std::endl;              // length=11
std::cout << "size=" << s1.size() << std::endl;                  // size=11

std::cout << "length bytes=" << s1.length_bytes() << std::endl;  // length bytes=11
std::cout << "size bytes=" << s1.size_bytes() << std::endl;      // size bytes=11

std::wstring str2 = L"Hello world";
gsl::cwstring_span<> s2 = str2;

std::cout << "length=" << s2.length() << std::endl;              // length=11
std::cout << "size=" << s2.size() << std::endl;                  // size=11

std::cout << "length bytes=" << s2.length_bytes() << std::endl;  // length bytes=22
std::cout << "size bytes=" << s2.size_bytes() << std::endl;      // size bytes=22

Subspans

It is possible to create subspans from a string_span. There are several functions that do that:

  • first(): returns the sub-span with the first N elements from the original string_span
  • last(): returns the sub-span with the last N elements from the original string_span
  • subspan(): returns the sub-span within the specified range (first and last positions) of the original string_span.
std::string str1 = "Hello world";
gsl::cstring_span<> s1 = str1;            // s1 = { 'H','e','l','l','o',' ','w','o','r','l','d' }
gsl::cstring_span<> l = s1.first(5);      // l = { 'H','e','l','l','o' }
gsl::cstring_span<> r = s1.last(5);       // r = { 'w','o','r','l','d' }
gsl::cstring_span<> m = s1.subspan(3, 5); // m = { 'l','o',' ','w','o' }

Comparisons

You can use the comparison operators (==, !=, <, <=, >, >=) with two string spans. Just like in the case of span<T>, equality is checked with std::equal (two ranges are equal if every element in the first range is equal to the element corresponding to the same position in the second range) and less/greater is checked with std::lexicographical_compare() (one range is less than another if the first mismatch element in the first range is less than the element on the same position in the second range).

std::string str1 = "Hello world";
gsl::cstring_span<> s1 = str1;

std::string str2 = "Hello world!";
gsl::cstring_span<> s2 = str2;

std::cout << (s1 == s2) << std::endl; // prints 0
std::cout << (s1 != s2) << std::endl; // prints 1
std::cout << (s1 < s2)  << std::endl; // prints 1
std::cout << (s1 > s2)  << std::endl; // prints 0

Element access

It is possible to access the content of a string_span either with iterators or indexes.

std::string str = "Hello world";
gsl::cstring_span<> s = str;

// prints Hello world
for (auto const & e : s) std::cout << e;
std::cout << std::endl;

// prints dlrow olleH
for (auto it = s.rbegin(); it != s.rend(); ++it) std::cout << *it;
std::cout << std::endl;

// prints Hello world
for (auto it = std::begin(s); it != std::end(s); ++it) std::cout << *it;
std::cout << std::endl;

// prints Hello world
for (ptrdiff_t i = 0; i < s.size(); ++i)
    std::cout << s[i];
std::cout << std::endl;

//s[5] = '-'; // Error	C3892	's': you cannot assign to a variable that is const
std::string str = "Hello world";
gsl::string_span<> s = str;

s[5] = '-';

// prints Hello-world
for (ptrdiff_t i = 0; i < s.size(); ++i)
   std::cout << s[i];
std::cout << std::endl;

4 thoughts on “Guidelines Support Library Review: string_span<T>”

  1. > span can be used with strings, but the Guidelines Support Library provides a different span implementation for various types of strings. These string span types are available in the string_span.h header.

    So span can only be used for strings, but string_span can be used for other types? A little confusing… could you perhaps clarify the differences between gsl::span, gsl::string_span and also std::span (c++17) ? When would you use which one and why? That would be really helpful! Thanks!

    Reply
    • What is confusing? I don’t find anything confusing. “span<T> can be used with strings”. I don’t understand why that became “span can only be used for strings” for you. There is a span type that can be used with various types of ranges including strings or char arrays, but there is also a string_span, that is a range of strings (and cannot be used with other types like ints).

      Reply
      • So afaict the unique properties are:

        gsl::span rw, multi-demensional, arbitrary alphabets
        gsl::string_span rw, one-dimensional, character alphabets, has a to_string() and a length() member

        And
        std::span / array_view / string_view would be like string_span except that it is read-only
        [http://stackoverflow.com/questions/34832090/whats-the-difference-between-span-and-array-view-in-the-gsl-library]

        Is that correct so far?

        Thank you for your reply!

        Reply

Leave a Comment