C++17 - What's New in the Library?

Contents[Show]

What's new in the library? A lot. To make the long story short. We get a std::string_view, parallel algorithm of the Standard Template Library, a file system library, and the three new data types std::any, std::optional, and std::variant. Let's look at the details.

 

timeline

Our journey starts with std::string_view.

std::string_view

A std::string_view is a non-owning reference to a string. It represents a view of a sequence of characters. This sequence of characters can be a C++-string or C-string. In a typical way C++17 offers four type synonyms for the underlying character-types.

std::string_view      std::basic_string_view<char>
std::wstring_view     std::basic_string_view<wchar_t>
std::u16string_view   std::basic_string_view<char16_t>
std::u32string_view   std::basic_string_view<char32_t>

The question remains. Why do we need a std::string_view? Why had Google, LLVM, and Bloomberg already an implementation of a string-view? The answer is easy. It's quite cheap to copy a std::string_view. A std::string_view only needs two information: the pointer to the character sequence and their length. As you may assume, the std::string_view and its three siblings consist mainly of reading operations that follow the interface of std::string. Mainly, because it gets the new methods remove_prefix and remove_suffix.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// string_view.cpp

#include <iostream>
#include <string>
#include <experimental/string_view>

int main(){
    
  std::string str = "   A lot of space";
  std::experimental::string_view strView = str;
  strView.remove_prefix(std::min(strView.find_first_not_of(" "), strView.size()));
  std::cout << "str      :  " << str << std::endl 
            << "strView  : " << strView << std::endl;
                 
  std::cout << std::endl;

  char arr[] = {'A',' ','l','o','t',' ','o','f',' ','s','p','a','c','e','\0', '\0', '\0'};
  std::experimental::string_view strView2(arr, sizeof arr);
  auto trimPos = strView2.find('\0');
  if(trimPos != strView2.npos) strView2.remove_suffix(strView2.size() - trimPos);
  std::cout << "arr     : " << arr << ", size=" << sizeof arr << std::endl
            << "strView2: " << strView2 << ", size=" << strView2.size() << std::endl;
              
}

 

The program should not surprise you. The std::string_view's in line 10 and 18 get their C++-string and character-array reference. In line 11, all leading non-spaces (strView.find_first_not_of(" ")) are removed and in line 20, all trailing "\0"-characters (strView2.find('\0")) are removed. By using the namespace experimental, I can already execute the program at cppreference.com.

 string view

Now, to something more familiar.

Parallel algorithm of the Standard Template Library

My story is quite short. 69 algorithms of the Standard Template Library (STL) will be available in a sequential, parallel and parallel and vectorized version. Additionally, we get 8 new algorithms. Have all look at the 69 new variants (black) and the 8 (red) new algorithms.

 stl

 

That was all. I already wrote a post Parallel Algorithm of the Standard Template Library. In contrary, the filesystem library should be new to you.

The filesystem library

The new filesystem library is based on boost::filesystem. Some of its components are optional. That means not all functionality of std::filesytem is available on each implementation of the filesystem library. For example, FAT-32 does not support symbolic links.

The library is based on the three concepts file, file name and path. Files can be directories, hard links, symbolic links or regular files. Paths can be absolute or relative.

There is a powerful interface for reading and manipulating the filesystem. Use cppreference.com for the details. Here is a first impression.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// filesystem.cpp

#include <fstream>
#include <iostream>
#include <string>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
 
int main(){

    std::cout << "Current path: " << fs::current_path() << std::endl;

    std::string dir= "sandbox/a/b";
    fs::create_directories(dir);

    std::ofstream("sandbox/file1.txt");
    fs::path symPath= fs::current_path() /=  "sandbox";
    symPath /= "syma";
    fs::create_symlink("a", "symPath");
    
    std::cout << "fs::is_directory(dir): " << fs::is_directory(dir) << std::endl;
    std::cout << "fs::exists(symPath): "  << fs::exists(symPath) << std::endl;
    std::cout << "fs::symlink(symPath): " << fs::is_symlink(symPath) << std::endl;
    

    for(auto& p: fs::recursive_directory_iterator("sandbox"))
        std::cout << p << std::endl;
    // fs::remove_all("sandbox");
    
}

 

fs::current_path() in line 11 returns the current path. You can create a directory hierarchy (line 14) with std::filesystem. Line 18 looks a little bit odd. The /= is overloaded for a path. Therefore I can directly create a symbolic link in line 19. You can check the properties of a file (line 21 - 23).   The call recursive_directory_iterator in line 26 is quite powerful. You can use it to recursively traverse directories. Of course, I can not remove a directory (line 28) on an online compiler.

Here is the output of the program.

filesystem

What has the new data type std::any, std::optional, and std::variant in common? They are based on boost.

std::any

std::any will be the right choice if you want to have an container with an arbitrary type. Arbitrary type is not hundred percent correct. std::any requires, that its values must be copyable. Here is a short example.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// any.cpp

#include <iostream>
#include <string>
#include <vector>
#include <any>

struct MyClass{};

int main(){
    
    std::cout << std::boolalpha;
    
    std::vector<std::any> anyVec(true,2017,std::string("test"),3.14,MyClass());
    std::cout << "std::any_cast<bool>anyVec[0]: " << std::any_cast<bool>(anyVec[0]); // true
    int myInt= std::any_cast<int>(anyVec[1]);                                        
    std::cout << "myInt: " << myInt << std::endl;                                    // 2017
    
    std::cout << std::endl;
    std::cout << "anyVec[0].type().name(): " << anyVec[0].type().name();             // b
    std::cout << "anyVec[1].type().name(): " << anyVec[1].type().name();             // i
    
}

 

The output of the program is in the source code. Line 14 defines a std::vector<std::any>. To get one of its elements, you have to use std::any_cast. If you use the wrong type, you will get an std::bad_any_cast exception. For further details, go to cppreferenc.com or wait for my additional post.

std::any can have values of arbitrary types, std::optional can have a value or no value.

std::optional

I will make it rather short. In the post Monads in C++, I already wrote about the monad std::optional.

The third new data type from boost is std::variant.

std::variant

A std::variant a type-safe union. An instance of std::variant has a value from one of its types. The type must not be a reference, array or void. A union can have one type more than once. A default-initialized std::variant will be initialized with its first type. Therefore, the first type must have in this case a default constructor. Here is an example, based on cppreference.com. 

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// variant.cpp

#include <variant>
#include <string>
 
int main(){

  std::variant<int, float> v, w;
  v = 12;                              // v contains int
  int i = std::get<int>(v);
  w = std::get<int>(v);
  w = std::get<0>(v);                  // same effect as the previous line
  w = v;                               // same effect as the previous line
 
  //  std::get<double>(v);             // error: no double in [int, float]
  //  std::get<3>(v);                  // error: valid index values are 0 and 1
 
  try{
    std::get<float>(w);                // w contains int, not float: will throw
  }
  catch (std::bad_variant_access&) {}
 
  std::variant<std::string> v("abc");  // converting constructors work when unambiguous
  v = "def";                           // converting assignment also works when unambiguous

}

 

I define in line 8 both variants v and w. Both can have an int and a float value. Their value is 0. v becomes 12 on line 9. std::get<int>(v) returns the value. In line 11-13 you see three possibilities to assign the variant v the variant w. But you have to keep a few rules in mind. You can ask for the value of a variant by type (line 15) or by index (line 16). The type must be unique and the index valid. On line 19, the variant w holds an int value. Therefore, I get a std::bad_variant_access exception in line 21. If the constructor call or assignment call is unambiguous, a conversion can take place. That is the reason that it's possible to construct a std::variant<std::string> in line 23 with a C-string or assign a new C-string to the variant (line 24).

 What's next?

I stick with the C++17 standard. After I gave in this and the last post an overview of the core language and the library, I will dive in the next post into the details (Proofreader Marc Bertola).

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

 

Tags: C++17

Comments   

0 #1 Alexander 2017-04-03 11:08
You mark "for_each()" as a new algorithm, but it already exists. According this link: https://isocpp.org/files/papers/p0636r0.html only 7 new algorithms :-)
Quote
0 #2 Rainer Grimm 2017-04-04 05:05
Quoting Alexander:
You mark "for_each()" as a new algorithm, but it already exists. According this link: https://isocpp.org/files/papers/p0636r0.html only 7 new algorithms :-)
You are right. I wanted only to mention for_each_n. I will write in a second article about the 7 new algorithms. Then, I will make it right.
Quote

Add comment


My Newest E-Book

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 50

All 362740

Currently are 140 guests and no members online