C++17- std::byte and std::filesystem

Contents[Show]

My post C++17 - What's New in the Library was fine for the first overview. Today, I will look deeper into the new library. 

 

Let's start with something totally new that I didn't mention in my previous posts:

timeline

std::byte

std::byte is a distinct type implementing the concept of byte as specified in the C++ language definition. Now we know, what a byte is.Therefore, a byte is not an integer or a character and therefore not open to programmer errors. Its job is to access object storage. Consequently, its interface consists only of methods for bitwise logical operations.

 

namespace std { 

  template <class IntType> 
    constexpr byte operator<<(byte b, IntType shift); 
  template <class IntType> 
    constexpr byte operator>>(byte b, IntType shift); 
  constexpr byte operator|(byte l, byte r); 
  constexpr byte operator&(byte l, byte r); 
  constexpr byte operator~(byte b); 
  constexpr byte operator^(byte l, byte r); 

} 

 

You can use the function std::to_integer(std::byte b) to convert a std::byte to an integer type and the call std::byte{integer} to do it the other way around. integer has to be a non-negative value smaller than std::numeric_limits<unsigned_char>::max().

Now to something, you already know.

The filesystem library

I gave you in the post C++17 - What's New in the Library a first impression of the filesystem library. 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, canonical or relative. A canonical path is a path including no symlinks, "." or ".." elements.

You can create and remove directories, iterate over them or check properties of files.

 

 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 <filesystem>
namespace fs = std::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");
    
}

 

But there is more to it. So I will now focus on features that are not so obvious. At least for me. I show you,

  • how you can manipulate the file permissions,
  • read the time values of a file,
  • and even get the free and available space of the filesystem.

I used for my examples the std::experimental::filesystem namespace. Therefore, I was able to run and check them on cppreference.com. Afterwards, I adjusted the sources to the upcoming official names. That means,  I replaced the header <experimental/filesystem> by <filesystem> and the namespace std::experimental::filesystem by std::filesystem.

Let's start with file permissions.

Permissions

The permission is represented by the class std::filesystem::perms. It is a BitmaskType and can, therefore, be manipulated by bitwise operations. The access permissions are based on POSIX.

The program from cppreference.com shows, how you can read and manipulate the owner, group, and other (world) bits of a file.

 

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
// perms.cpp

#include <fstream>
#include <bitset>
#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;
 
void printPerms(fs::perms perm){
  std::cout << ((perm & fs::perms::owner_read) != fs::perms::none ? "r" : "-")
            << ((perm & fs::perms::owner_write) != fs::perms::none ? "w" : "-")
            << ((perm & fs::perms::owner_exec) != fs::perms::none ? "x" : "-")
            << ((perm & fs::perms::group_read) != fs::perms::none ? "r" : "-")
            << ((perm & fs::perms::group_write) != fs::perms::none ? "w" : "-")
            << ((perm & fs::perms::group_exec) != fs::perms::none ? "x" : "-")
            << ((perm & fs::perms::others_read) != fs::perms::none ? "r" : "-")
            << ((perm & fs::perms::others_write) != fs::perms::none ? "w" : "-")
            << ((perm & fs::perms::others_exec) != fs::perms::none ? "x" : "-")
            << std::endl;
}


int main(){
  
    std::ofstream("rainer.txt");
 
    std::cout << "Initial file permissions for a file: ";
    printPerms(fs::status("rainer.txt").permissions());
 
    fs::permissions("rainer.txt", fs::perms::add_perms |
                            fs::perms::owner_all | fs::perms::group_all);
    std::cout << "Adding all bits to owner and group:  ";
    printPerms(fs::status("rainer.txt").permissions());
    
    fs::permissions("rainer.txt", fs::perms::remove_perms | 
                           fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write);
    std::cout << "Removing the write bits for all:     ";
    printPerms(fs::status("rainer.txt").permissions());
 
    fs::remove("rainer.txt");
    
}

 

I created in line 26 a new file. Thanks to the global function std::filesystem::status::permissions, I get the permissions of the file and can display them in the function printPerms (line 10-21). After I set the constant, std::filesystem::add_perms in line 31, I can add permissions to the owner and the group of the file. Doing it the other way around, I set the constant std::filesystem::remove_perms in line 36. Therefore, I can remove the write bits for all.

Here is the output of the program.

perms

A file has not only the notion of permission but also of time.

Time values

Thanks to the global function std::filesystem::last_write_time, I can read and write the last write time of a file. Here is the example, based on the example of en.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
27
28
29
// fileTime.cpp

#include <iostream>
#include <chrono>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;
using namespace std::chrono_literals;

int main(){
    
    fs::path path = fs::current_path() / "rainer.txt";
    std::ofstream(path.c_str()); 
    auto ftime = fs::last_write_time(path);
 
    std::time_t cftime = std::chrono::system_clock::to_time_t(ftime); 
    std::cout << "Write time on server " << std::asctime(std::localtime(&cftime));
    std::cout << "Write time on server " << std::asctime(std::gmtime(&cftime)) << std::endl;
 
    fs::last_write_time(path, ftime + 2h);
    ftime = fs::last_write_time(path); 
 
    cftime = std::chrono::system_clock::to_time_t(ftime);
    std::cout << "Local time on client " << std::asctime(std::localtime(&cftime)) << std::endl;
    
    fs::remove(path);
    
}

 

In line 15, I get the write time of the newly created file. I use ftime in line 17 to initialize std::chrono::system_clock. ftime is of type std::filesystem::file_time_type which seem on the server an alias for std::chrono::system_clock. That is fine. Therefore, I can initialize std::localtime in line 18 and present the calendar time in a textual representation. If I use std::gmtime instead of std::localtime (line 18), nothing will change. That puzzled me because the Coordinated Universal Time (UTC) differs 2 hours from the local time in German. But that's okay because that will not hold for the server. UTS and local time are the same on the server. 

Here is the output of the program. In addition, you see the local time in Germany. I got it by adding 2 hours (line 21) to the last write time of the file.

 fileTime

Now to the feature that astonished me the most.

Space info

The global function std::filesystem::space returns a std::filesystem::space_info object that has the three members capacity, free, and available.

  • capacity: total size of the filesystem
  • free: free space on the filesystem
  • available: free space to a non-privileged process (maybe equal or less than free)

All sizes are in bytes. The output of the following program is from cppreference.com. All the paths I tried were on the same filesystem. Therefore, I always get the same answer.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// space.cpp

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main(){
    
    fs::space_info root = fs::space("/");
    fs::space_info usr = fs::space("/usr");
 
    std::cout << ".        Capacity       Free      Available\n"
              << "/    " << root.capacity << "   "
              << root.free << "   " << root.available << "\n"   
              << "usr  "  << usr.capacity << "   "
              << usr.free <<  "   " << usr.available;
              
}

 

 Here are the numbers.

space

More Information

What's next?

Our journey through the details of C++17 goes on. The next post will continue with std::string_view.

 

 

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.

 

Comments   

0 #1 Tania 2017-07-18 06:26
Way cool! Some extremely valid points! I appreciate you penning this write-up and the rest of
the website is really good.
Quote

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 1657

All 535134

Currently are 180 guests and no members online