trainings/AdvancedCppV2/Presentation/moder_cpp_cpp17_filesystem.md

341 lines
No EOL
13 KiB
Markdown

## Filesystem
<!-- .slide: data-background="#ccc" -->
Since C++17 we can efficiently work with the file systems on all OS like Mac, Windows, and various Linux distributions.
```C++
namespace fs = std::filesystem;
void printStatsForDirectory(const fs::path& dir_path) {
std::cout << "Statistics:\n";
std::cout << ".cpp files: "
<< std::count_if(fs::recursive_directory_iterator(dir_path), {},
[](const auto& entry) {
return entry.is_regular_file() && entry.path().extension() == ".cpp";
}) << '\n';
std::cout << ".md files: "
<< std::count_if(fs::recursive_directory_iterator(dir_path), {},
[](const auto& entry) {
return entry.is_regular_file() && entry.path().extension() == ".md";
}) << '\n';
std::cout << ".h files: "
<< std::count_if(fs::recursive_directory_iterator(dir_path), {},
[](const auto& entry) {
return entry.is_regular_file() && entry.path().extension() == ".h";
}) << '\n';
std::cout << "Total size: "
<< (std::accumulate(fs::recursive_directory_iterator(dir_path), {}, 0,
[](const auto& init, const auto& entry) {
return init + (entry.is_regular_file() ? entry.file_size() : 0);
}) >> 20) << " MB\n";
}
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Output
```C++
int main() {
const auto path{fs::current_path()};
std::cout << "current path: " << path << '\n';
const auto path2 = path.parent_path().parent_path();
std::cout << "path2: " << path2 << '\n';
printStatsForDirectory(path2);
}
```
```bash
current path: "C:\\Users\\madamski\\Documents\\altcom_academy\\Nokia2022\\Advanced\\Course_part1\\exercises\\streamer"
path2: "C:\\Users\\madamski\\Documents\\altcom_academy\\Nokia2022\\Advanced\\Course_part1"
Statistics:
.cpp files: 16
.md files: 53
.h files: 47
Total size: 103 MB
```
<!-- .element: class="fragment fade-in" -->
<!-- .slide: style="font-size: 0.75em" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Info about system space
```C++
void printSpaceInfo(auto const& dirs, int width = 10) {
std::cout << std::left;
for (const auto s : {"Capacity", "Free", "Available", "Dir"}) {
std::cout << "| " << std::setw(width) << s << std::string(3, ' ');
}
std::cout << '\n';
for (auto const& dir : dirs) {
const auto info = fs::space(dir);
std::cout
<< "| " << std::setw(width) << (info.capacity >> 30) << " GB"
<< "| " << std::setw(width) << (info.free >> 30) << " GB"
<< "| " << std::setw(width) << (info.available >> 30) << " GB"
<< "| " << dir << '\n';
}
}
int main() {
const auto dirs = {"C:\\", "D:\\", "E:\\"};
printSpaceInfo(dirs);
/*
| Capacity | Free | Available | Dir
| 237 GB| 15 GB| 15 GB| C:\
| 383 GB| 179 GB| 179 GB| D:\
| 463 GB| 220 GB| 220 GB| E:\
*/
}
```
<!-- .slide: style="font-size: 0.72em" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Permissions
```C++
void printPerms(fs::perms p) {
std::cout << ((p & fs::perms::owner_read) != fs::perms::none ? "r" : "-")
<< ((p & fs::perms::owner_write) != fs::perms::none ? "w" : "-")
<< ((p & fs::perms::owner_exec) != fs::perms::none ? "x" : "-")
<< ((p & fs::perms::group_read) != fs::perms::none ? "r" : "-")
<< ((p & fs::perms::group_write) != fs::perms::none ? "w" : "-")
<< ((p & fs::perms::group_exec) != fs::perms::none ? "x" : "-")
<< ((p & fs::perms::others_read) != fs::perms::none ? "r" : "-")
<< ((p & fs::perms::others_write) != fs::perms::none ? "w" : "-")
<< ((p & fs::perms::others_exec) != fs::perms::none ? "x" : "-")
<< '\n';
}
int main() {
for (const auto& entry : fs::directory_iterator(fs::current_path())) {
if (fs::is_regular_file(entry.path())) {
std::cout << "File: "
<< std::left << std::setw(20)
<< entry.path().filename()
<< " | Permissions: ";
printPerms(fs::status(entry.path()).permissions());
}
}
/*
File: "CMakeLists.txt" | Permissions: rw-rw-rw-
File: "example.exe" | Permissions: rwxrwxrwx
File: "README.md" | Permissions: rw-rw-rw-
File: "streamer.cpp" | Permissions: rw-rw-rw-
*/
}
```
<!-- .slide: style="font-size: 0.72em" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Exercise
Open project `files`. Write a program which modify files under `project` directory:
- Change permision for all .exe files to `rwxrw-rw-`.
- Change permision for all .cpp and .hpp files to `rw-r--r--`.
You should get output like this:
<!-- .element: class="fragment fade-in" -->
```Bash
File: "fileA.cpp" | Permissions: rw-------
File: "fileA.hpp" | Permissions: rw-------
File: "fileB.cpp" | Permissions: rw-------
File: "fileB.hpp" | Permissions: rw-------
File: "fileC.cpp" | Permissions: rw-------
File: "fileC.hpp" | Permissions: rw-------
File: "project.exe" | Permissions: rw-------
File: "fileA.cpp" | Permissions: rw-r--r--
File: "fileA.hpp" | Permissions: rw-r--r--
File: "fileB.cpp" | Permissions: rw-r--r--
File: "fileB.hpp" | Permissions: rw-r--r--
File: "fileC.cpp" | Permissions: rw-r--r--
File: "fileC.hpp" | Permissions: rw-r--r--
File: "project.exe" | Permissions: rwxrw-rw-
```
<!-- .slide: style="font-size: 0.78em" -->
<!-- .element: class="fragment fade-in" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Exercise 2
Open project `files`. Modify `FileB` by append any word. Now write a program which:
- return last modify files
- return a vector with 3 files that were longest unmodified.
Expected result (path may by different):
<!-- .element: class="fragment fade-in" -->
```Bash
Last modified file is: "/home/runner/SilentSiennaModularity/files/project/fileB.cpp"
File write time is Sat May 14 17:56:18 2022
files that were longest unmodified:
File: "/home/runner/SilentSiennaModularity/files/project/fileA.cpp" | last modify time: Sat May 14 16:43:33 2022
File: "/home/runner/SilentSiennaModularity/files/project/fileA.hpp" | last modify time: Sat May 14 16:43:33 2022
File: "/home/runner/SilentSiennaModularity/files/project/fileB.hpp" | last modify time: Sat May 14 16:43:33 2022
```
<!-- .element: class="fragment fade-in" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Problems and fixes
- <!-- .element: class="fragment fade-in" --> Remember that <code>recursive_directory_iterator</code> and <code>directory_iterator</code> are <code>InputIterator</code>. If you move iterator to the next position you invalidate all reference to previous object. If you need to use algorithm which demand <code>ForwardIterator</code> you can copy all paths to separate container
- <!-- .element: class="fragment fade-in" --> If you want to keep sorted files inside <code>set</code> or <code>map</code> you should check whether their modification times are the same. If true you need to compare their names or files with the same modification time will be lost.
- <!-- .element: class="fragment fade-in" --> Setup premissions for files works only on Linux/ MacOS system. This is caused by different type of File system. Windows file system mainly NTFS do not support priviliges like in other systems eg. EXT4. There are still properites but handled in different way:
- <!-- .element: class="fragment fade-in" --> Full Control: Grants complete access, including the ability to see, read, write, execute and delete files or folders, as well as change permission settings for all subdirectories.
- <!-- .element: class="fragment fade-in" --> Modify: The user can see, read, execute, write and delete files. Also allows for the deletion of the folder itself.
- <!-- .element: class="fragment fade-in" --> Read & Execute: Can view folder contents and run programs or scripts.
- <!-- .element: class="fragment fade-in" --> List folder contents: Allows the user to see files and directories contained within a folder, an important setting for navigating to deeper levels in the folder structure.
- <!-- .element: class="fragment fade-in" --> Read: Can see folder contents and also view the files and folders in question.
- <!-- .element: class="fragment fade-in" --> Write: Users can add new files and folders and write to existing files.
- <!-- .element: class="fragment fade-in" --> Special permissions: Additional permissions available through the Advanced Security Settings in the Windows file system. Includes options such as Read Attributes, Create Files or Traverse Folder.
<!-- .slide: style="font-size: 0.86em" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
## Temporary files for testing
Sometimes during a test we need to read input data from file. usually we need to create this file and attach to test directory. But since C++17 we can easily use temporary directory to create all files. let's test this class: (The main resposibility is to queued all files and stream them)
```C++
class Streamer {
public:
Streamer() = default;
virtual ~Streamer() = default;
Streamer(const Streamer&) = default;
Streamer(Streamer&&) = default;
Streamer& operator=(const Streamer&) = default;
Streamer& operator=(Streamer&&) = default;
virtual void stream() = 0;
virtual void stop() = 0;
};
```
___
<!-- .slide: data-background="#ccc" -->
<div class="multicolumn">
<div class="col">
```C++
class MpgegStreamer : public Streamer {
public:
MpgegStreamer(const fs::path& path)
: path_(path) {
for (int i = 0; i < 4; ++i) {
workers_.emplace_back(&MpgegStreamer::work, this, i);
}
}
~MpgegStreamer() override {
for (auto& th : workers_) {
th.join();
}
}
void stream() override {
for (const auto& entry : fs::directory_iterator(path_)) {
std::lock_guard lg(m_);
streamQueue_.push(entry.path());
}
}
virtual void stop() {
finishAction_ = true;
}
```
</div>
<div class="col">
```C++
private:
void work(int id) {
while (!finishAction_) {
fs::path path;
{
std::lock_guard lg(m_);
// Ofc this will be better with condition variable
if (streamQueue_.empty()) {
continue;
}
path = std::move(streamQueue_.front());
streamQueue_.pop();
}
stream(path, id);
}
}
void stream(const fs::path path, int id) const {
std::fstream fileToStream(path);
std::transform(
std::istream_iterator<std::string>(fileToStream),
{},
std::ostream_iterator<std::string>(std::cout),
[id](const std::string& str) {
std::stringstream ss;
ss << "Streamer: " << id << " | data: " << str << '\n';
return ss.str();
});
}
mutable std::mutex m_;
std::vector<std::thread> workers_;
std::queue<fs::path> streamQueue_;
fs::path path_;
std::atomic<bool> finishAction_{false};
};
```
</div>
</div>
<!-- .slide: style="font-size: 0.52em" -->
___
<!-- .slide: data-background="#ccc" --><!-- .slide: data-background="#ccc" -->
We can create temporary direcotories (yes we can create them recursive!) and cleanup at the end. We don't need to be afraid of removing sth neccessary because we will work only with tmoprary directories.
```C++
int main() {
const auto tmp_path = fs::temp_directory_path();
const auto mpegDir = tmp_path.string() + "Test/MpgegFiles";
const auto mp4Dir = tmp_path.string() + "Test/MP4Files";
// Create test directory
fs::create_directories(mpegDir);
fs::create_directory(mp4Dir);
// Add files
for (int i = 0; i < 10; ++i) {
std::ofstream file(mpegDir + "/File" + std::to_string(i));
file << "this is some text in the new file\n" << i;
}
for (int i = 0; i < 10; ++i) {
std::ofstream file(mp4Dir + "/File" + std::to_string(i));
file << "this is some text in the new file\n" << i;
}
std::cout << "Start streaming" << std::endl;
std::unique_ptr<Streamer> streamer = std::make_unique<MpgegStreamer>(mpegDir);
streamer->stream();
std::this_thread::sleep_for(std::chrono::seconds(1));
streamer->stop();
fs::remove_all(tmp_path.string() + "Test");
}
```
<!-- .slide: style="font-size: 0.70em" -->