C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн
С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.
5. Рассмотрим работу с функцией canonical и составными путями. Передавая функции canonical относительный путь к файлу, например "foobar.txt", и составной абсолютный путь current_path()/"testdir", получаем существующий абсолютный путь к файлу. В следующем обращении к функции передаем наш путь p (т.е. "testdir/foobar.txt") и абсолютный путь current_path(), который направляет нас в каталог "testdir" и обратно. Это то же самое, что и current_path(), из-за косвенных адресов. В обоих вызовах функция canonical должна возвратить одинаковый абсолютный путь.
cout << "canonical testdir : "
<< canonical("foobar.txt",
current_path()/"testdir")
<< "ncanonical testdir 2 : "
<< canonical(p, current_path()/"testdir/..")
<< 'n';
6. Кроме того, можно проверить эквивалентность двух путей, не являющихся каноническими. Функция equivalence приводит к каноническому виду пути к файлам, которые она принимает в качестве аргументов, и в конечном итоге возвращает значение true при условии, что они описывают один и тот же путь. Для этой проверки путь к файлу должен действительно существовать, в противном случае функция сгенерирует исключение.
cout << "equivalence: "
<< equivalent("testdir/foobar.txt",
"testdir/../testdir/foobar.txt")
<< 'n';
}
7. Компиляция и запуск программы дадут следующий результат. Функция current_path() возвращается к домашнему каталогу на моем ноутбуке, поскольку я запустил приложение оттуда. К нашему относительному пути p был добавлен префикс, состоящий из данной папки, с помощью функций absolute_path, system_complete и canonical. Мы видим, что функции absolute_path и system_complete выдают абсолютно одинаковое описание пути файла в моей системе, потому что это Mac (на Linux будет так же). В компьютере с операционной системой Windows функция system_complete добавит префикс "C:" или любого другого диска, в котором расположен рабочий каталог.
$ ./canonical_filepath
current_path : "/Users/tfc"
absolute_path : "/Users/tfc/testdir/foobar.txt"
system_complete : "/Users/tfc/testdir/foobar.txt"
canonical(p) : "/Users/tfc/testdir/foobar.txt"
"testdir/foobar.txt"
canonical testdir : "/Users/tfc/testdir/foobar.txt"
canonical testdir 2 : "/Users/tfc/testdir/foobar.txt"
equivalence: 1
8. Мы не обрабатываем никаких исключений в нашей короткой программе. При удалении файла foobar.txt из каталога testdir программа прекращает свою работу из-за исключения. Функция canonical требует наличия действительного пути файла. Существует также функция weakly_canonical, которая не предъявляет подобных требований.
$ ./canonial_filepath
current_path : "/Users/tfc"
absolute_path : "/Users/tfc/testdir/foobar.txt"
system_complete : "/Users/tfc/testdir/foobar.txt"
terminate called after throwing an instance of
'std::filesystem::v1:: cxx11::filesystem_error'
what(): filesystem error: cannot canonicalize:
No such file or directory [testdir/foobar.txt] [/Users/tfc]
Как это работает
Цель данного примера заключается в том, чтобы увидеть, как легко создавать новые пути динамически. В основном это связано с наличием в классе path удобного перегруженного оператора /. Кроме того, функции файловой системы хорошо работают с абсолютными и относительными путями к файлам, а также с путями, которые содержат косвенные адреса . и ...
Есть довольно много функций, которые возвращают части экземпляра path, иногда даже преобразуя их. Не будем перечислять все существующие функции, поскольку для знакомства с ними лучше всего обратиться к справочным материалам по C++.
Однако функции-члены класса path, возможно, стоит рассмотреть поближе. Посмотрим, каким функциям-членам класса path соответствуют конкретные части пути к файлу. На следующей диаграмме показано, что пути файлов в Windows несколько отличаются от путей файлов в UNIX/Linux (рис. 10.1).
Как видите, функции-члены класса path возвращаются для абсолютного пути. Для относительных путей root_path, root_name и root_directory пусты. relative_ path, соответственно, возвращает путь, только если тот уже является относительным.
Составляем список всех файлов в каталоге
Конечно же, каждая операционная система, предлагающая поддержку файловой системы, также поставляется с утилитой, которая просто перечисляет все файлы внутри каталога в файловой системе. Самые простые примеры — команда ls в Linux, MacOS и других UNIX-подобных операционных системах. В DOS и Windows существует команда dir. Обе команды составляют список из всех файлов в каталоге и предоставляют дополнительную информацию, такую как размер файла, разрешения и т.д.
Однако переопределение такого инструмента также является хорошим примером, который позволит нам научиться выполнять обходы каталогов и файлов. Давайте просто сделаем это!
Наша собственная утилита ls/dir будет способна упорядочивать по имени все файлы в каталоге, их флаги разрешения доступа и отображать количество байт, которые они занимают в файловой системе.
Как это делается
В этом примере мы реализуем небольшой инструмент, который составляет список файлов в любом предоставленном пользователем каталоге. Он будет упорядочивать файлы в списке не только по имени, но и по типу, размеру и разрешениям доступа.
1. Сначала включим необходимые заголовочные файлы и объявим об использовании пространств имен std и filesystem по умолчанию:
#include <iostream>
#include <sstream>
#include <iomanip>
#include <numeric>
#include <algorithm>
#include <vector>
#include <filesystem>
using namespace std;
using namespace filesystem;
2. Понадобится вспомогательная функция file_info. Она принимает ссылку на объект directory_entry и извлекает из нее путь, а также объект file_status (с помощью функции status), который содержит тип файла и информацию о правах. Наконец, она извлекает и размер записи, если это обычный файл. Для каталогов и особых файлов мы просто возвращаем значение 0. Вся информация упаковывается в кортеж.
static tuple<path, file_status, size_t>
file_info(const directory_entry &entry)
{
const auto fs (status(entry));
return {entry.path(),
fs,
is_regular_file(fs) ? file_size(entry.path()) : 0u};
}
3. Кроме того, понадобится вспомогательная функция type_char. Путь может представлять не только каталоги и простые текстовые/бинарные файлы. Операционные системы предоставляют множество разнообразных типов, которые абстрагируют что-то еще, например интерфейсы аппаратных устройств в виде так называемых символьных/блочных файлов. В библиотеке для работы с файловой системой, расположенной в STL, есть множество функций-предикатов для них. Таким образом, можно вернуть букву 'd' для каталогов, букву 'f' для обычных файлов и т.д.:
static char type_char(file_status fs)
{
if (is_directory(fs)) { return 'd'; }
else if (is_symlink(fs)) { return 'l'; }
else if (is_character_file(fs)) { return 'c'; }