Stefano Lusardi, Software Developer
The filesystem library allows to perform operations on file systmem components.
Filesystem is based on the three elements:
Simple filesystem examples
#include <filesystem>
namespace fs = std::filesystem;
Retrieve paths
fs::path path1 = fs::current_path();
fs::path path2 = "/path/to/myFile.cf";
fs::path path3 = "C:\\path\\to\\my\\Folder\\";
Create/Delete nested directories
std::string myDir = "root/first/second";
bool isCreated = fs::create_directories(myDir);
auto numDeletedFolders = fs::remove_all("root"); // recursive
Basic file information
bool a = fs::exists(myDir)
bool b = fs::is_directory(myDir)
bool c = fs::is_symlink(myDir)
Permissions
The class fs::perms can hold values such as: owner_read, owner_write, group_read, group_write, ...
fs::perms is a bitmask type, so you can read its value as:
fs::perms myPerms = fs::status("myFile.txt").permissions();
bool canOwnerRead =
(perm & fs::perms::owner_read) != fs::perms::none;
Read/Write times
fs::path myPath = fs::current_path() / "myFile.txt";
std::ofstream(path.c_str());
auto writeTime = fs::last_write_time(path);
Space information (capacity, free, and available):
fs::space_info root_info = fs::space("/");
std::cout << root_info.capacity;
std::cout << root_info.free;
std::cout << root_info.available;
std::optional<T>
It is a template class that can wrap any type T and it is able to indicate if the wrapped value is initialized or not.
std::optional<int> opt1 = std::make_optional<int>(42);
std::optional<int> opt2 { 42 }; // Implicit initialization
Example: check if a function returns a valid value
std::optional<std::string> getString(bool ok) {
if (ok) { return "OptionalString"; }
return { }; // create an empty std::optional<std::string>
}
auto str1 = getString(true);
std::cout << str1.value(); // "OptionalString"
auto str2 = getString(false);
std::cout << str2.value(); // throws std::bad_optional_access
std::cout << str2.value_or("NoString"); // "NoString"
if (str2.has_value()) { /*...*/ } // explicit safety check
if (str2) { /*...*/ } // (bool conversion) implicit safety check
std::variant<T>
Template class that enforces a type-safe union.
It can hold a value of one of its alternative types, or none
std::variant<int, std::string> var = 42;
std::cout << std::get<int>(var); // "42"
var = "myString"s;
std::cout << std::get<std::string>(var); //"myString"
int x = std::get<int>(var); // throws std::bad_variant_access
Now you can forget about old (and unsafe) unions
Note that it is now possible to implement easily a visitor pattern using std::visit
Example: check current type
std::variant<int, std::string> var { "myString"; }
std::cout << std::holds_alternative<int>(var); // false
std::cout << std::holds_alternative<std::string>(var); // true
Example: try to get the current value
std::variant<int, float> var { 42 };
if(auto pvar = std::get_if<int>(&var))
std::cout << "variant value: " << *pvar; // "42"
else
std::cout << "failed to get value!";
std::any
Class template that can wrap a value of any type in a type-safe way.
std::any a(42); // initialize with an int
a = std::string("MyString"); // store a std::string
std::cout << std::any_cast<std::string>(a); // "MyString"
std::cout << std::any_cast<int>(a); // throws std::bad_any_cast
Before C++17 it was possible to achieve a similar behavior
using void* but this is not type-safe (dangerous!)
Example: check current type
std::any = 42;
if (a.has_value())
std::cout << a.type().name(); // "i" (int)
else
a.reset(); // destroy wrapped object
if (a.type() == typeid(int))
std::cout << "int: " << std::any_cast<int>(a);
else if (a.type() == typeid(std::string))
std::cout << "string: " << std::any_cast<std::string>(a);
if/switch initialization statement
It has always been possible to initialize a variable directly in a for loop as:
for (int i = 0; i < SIZE; ++i) { /*...*/ }
This is very convenient and safe: it prevents that the i variable is used outside the loop scope.
Now it is possible to do the same with both if and switch statements.
Example: in-scope if initialization
if (const auto key = getKey(); key!=42) { /* use key */ }
else { /* use key */ }
// "key" is no longer defined here
std::map<int, std::string> myMap {{0, "z"}, {42, "f"}};
if (const auto it = myMap.find(42); it != myMap.end())
std::cout << it->second; // "f"
The variable declared in the if (or switch) statement can only be used in the if or else branch since it is not visible to the outer scope.
Structured Bindings
Create a set of "alias names" from a tuple or from a struct
Example: C++11 binding with std::tie
std::set<int> mySet { };
// can't be declared const nor reference types
std::set<int>::iterator iter;
bool isInserted;
std::pair<std::set<int>::iterator, bool> it = mySet.insert(0)
iter = it.first;
isInserted = it.second;
std::tie(iter, isInserted) = mySet.insert(42);
std::cout << isInserted; // "true"
std::tie(std::ignore, isInserted) = mySet.insert(42);
std::cout << isInserted; // "false"
Example: C++17 Structured Bindings
std::set<int> mySet { };
const auto& [iter, isInserted] = mySet.insert(42);
// Now "isInserted" can be const
// "iter" and "isInserted" can be reference types
Example: get values from a struct
struct Point2D { int x; int y; };
auto p = Point2D(1, 2);
const auto& [lat, lon] = p;
std::cout << lat; // 1
std::cout << lon; // 2
Example: iterate on a map
std::map<int, char> myMap {{1, 'a'}, {2, 'b'}};
for (const auto& [key, val] : myMap)
{
std::cout << key << val;
}
// 1'a'
// 2'b'
Example: combine with if-init
if (auto [iter, isInserted] = mySet.insert(value); isInserted)
{
/* value has been inserted at position "iter" */
}
else
{
/* value has not been inserted */
}
// "iter" and "isInserted" are out of scope here
Multi-Value return
Function returning tuple that can be unpacked using Structured Bindings
No more need to pass input reference to functions to retrieve multiple outputs
Example: tuple-return
std::tuple<int, std::string, float> func(){
/* ... */
return { 42, "myString", 1.234}
}
const auto& [a, b, c] = func();
std::cout << a; // 42
std::cout << b; // "myString"
std::cout << c; // 1.234
Example: combine with if-init
if (const auto& [a, b, c] = func(); a>41)
{ /* ... */ }