timeline

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

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 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.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    The filesystem library

    In the post, C++17 – What’s New in the Library, I gave a first impression of the filesystem library. The library uses 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 includes no symlinks, “.” or “..” elements.

    You can create and remove directories, iterate over them, or check the 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 will 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. Afterward, I adjusted the sources to the upcoming official names. That means I replaced the header <experimental/filesystem> with <filesystem> and the namespace std::experimental::filesystem with std::filesystem.

    Let’s start with file permissions.

    Permissions

    The class std::filesystem::perms represent the permission. It is a BitmaskType and can 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 file’s permissions and can display them in the function printPerms (lines 10-21). After I set the constant, std::filesystem::add_perms in line 31, I can add permissions to the owner and the file group. Doing it the other way around, I set the constant std::filesystem::remove_perms in line 36. Therefore, I can remove the written 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 right time for 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 seems on the server 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. Nothing will change if I use std::gmtime instead of std::localtime (line 18). 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 with 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.

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *