Данная библиотека содержит компоненты, которых мне не хватает в Стандартной библиотеке C++. Это, своего рода, мой личный Boost.
Дополнительно, это уровень абстракции, который скрывает детали реализации, ошибки и причуды различных ОС, компиляторов и реализаций Стандартной библиотеки.
Структурными единицами самого верхнего уровня в библиотеке являются заголовочные файлы и файл библиотеки (или архив) - lib__vic.a или __vic.lib. Обычно файл библиотеки имеет дополнительный суффикс, вроде lib__vic14.a. Все заголовочные файлы располагаются в подкаталоге __vic/. Включать их следует следующим образом:
#include <__vic/header.h>
Где header.h - это имя нужного заголовочного файла.
Практически весь код находится внутри пространства имён __vic, включая другие пространства имён.
При компоновке программы, нужно подсунуть компоновщику файл библиотеки. Например:
$ g++ -std=c++14 prog.cpp -l__vic14
Библиотека может быть собрана с использованием одного из стандартов ISO C++: C++98, C++11, C++14, C++17, C++20 или C++23. Суффикс стандарта используется в качестве суффикса файла библиотеки (архива).
Некоторые компоненты библиотеки требуют какую-то минимальную версию стандарта, например многие требуют использования как минимум C++11. Такие компоненты помечаются значком [C++11], который означает «C++11 или более старшие версии».
Некоторые компоненты библиотеки доступны только для какой-то конкретной версии стандарта и не доступны для других. Такие компоненты помечаются значком [C++98 only].
Детальное описание компонентов библиотеки приведено в последующих главах. Описание сгруппировано по заголовочным файлам. В основном используется синтаксис C++23, как более богатый и выразительный.
Различные фундаментальные определения.
Литерал для пустого указателя. Может использоваться вместо NULL или 0. В режиме ISO C++ 98 определён как
const int nullptr = 0;
Данное определение позволяет писать код в стиле C++11 при использовании стандарта C++98.
Это одно из немногих глобальных определений библиотеки. Определение можно предотвратить, определив макрос __VIC_NO_NULLPTR_DEF перед включением.
int *p = nullptr; pthread_create(&tid, nullptr, thread_func, nullptr);
В режиме C++98 - макроопределение, синоним throw(). В режимах остальных стандартов данное определение отсутсвует.
template<class T, size_t N> constexpr size_t array_size(T (&array)[N]);
Возвращает количество элементов массива. Может вычисляться во время компиляции.
int m[] = { 1, 2, 3, 5, 7 }; size_t n = __vic::array_size(m); // n == 5 int *dup = new int[n];
class non_copyable { non_copyable(const non_copyable &) = delete; non_copyable &operator=(const non_copyable &) = delete; protected: non_copyable() = default; };
Наследование данного класса подавляет генерацию копирующих конструктора и присваивания. Аналог boost::noncopyable.
class C : private __vic::non_copyable { }; C c1; C c2 = c1; // Error! Non-copyable object
class non_heap_allocatable { void *operator new(std::size_t ) = delete; void *operator new(std::size_t , const std::nothrow_t & ) = delete; void *operator new[](std::size_t ) = delete; void *operator new[](std::size_t , const std::nothrow_t & ) = delete; protected: non_heap_allocatable() = default; };
Наследование данного класса предотвращает размещение объекта в куче с использованием оператора new.
class C : private __vic::non_heap_allocatable { }; C c; // Ok. Allocation on stack C *p = new C; // Error! Attempt to allocate on heap
Данный заголовочный файл всегда включает эти утилиты в режиме C++11.
Макроопределение для #include. Раскрывается в имя заголовочного файла, который содержит определение std::swap(), в зависимости от используемого стандарта языка.
#include __VIC_SWAP_HEADER
Библиотека предоставляет набор макроопределений, позволяющий программе определить, для какой целевой платформы она собирается. Подобные определения предоставляются компиляторами, но везде под разными именами. Использование макроопределений библиотеки позволяет не зависеть от компилятора.
Список аппаратных платформ (процессоров):
Другие макроопределения:
Обобщённые алгоритмы.
template< std::forward_iterator Iter, std::predicate<std::iter_value_t<Iter>> Pred > Iter skip_if_front(Iter begin, Iter end, Pred pred);
Пропускает все элементы в начале последовательности, удовлетворяющие данному предикату, и возвращает начало получившейся последовательности.
template< std::bidirectional_iterator Iter, std::predicate<std::iter_value_t<Iter>> Pred > Iter skip_if_back(Iter begin, Iter end, Pred pred);
Пропускает все элементы в конце последовательности, удовлетворяющие данному предикату, и возвращает конец получившейся последовательности.
Быстрые, компактные и независимые от locale инструменты для обработки ASCII-символов. Все инструменты находятся в пространстве имён __vic::ascii.
namespace ascii { constexpr bool isdigit(char c); constexpr bool isxdigit(char c); constexpr bool islower(char c); constexpr bool isupper(char c); constexpr bool isalpha(char c); constexpr bool isalnum(char c); constexpr bool isspace(char c); constexpr bool isblank(char c); constexpr bool isprint(char c); constexpr bool isgraph(char c); constexpr bool ispunct(char c); constexpr bool iscntrl(char c); constexpr bool isascii(char c); }
Аналоги соответствующих функций из <cctype>.
constexpr char ascii::todigit(int d);
Преобразует целое значение от 0 до 9 в десятичную цифру. Результат неопределён, если входное значение не попадает в указанный диапазон.
namespace ascii { constexpr char toxdigit_upper(int d); constexpr char toxdigit_lower(int d); }
Преобразует целое значение от 0 до 15 в шестнадцатеричную цифру. Первая функция использует верхний регистр для A-F, вторая - нижний. Результат неопределён, если входное значение не попадает в указанный диапазон.
constexpr int ascii::digit_to_number(char d);
Преобразует десятичную цифру в число, если ascii::isdigit(d). В противном случае возвращается -1.
constexpr int ascii::xdigit_to_number(char d);
Преобразует шестнадцатеричную цифру в число, если ascii::isxdigit(d). В противном случае возвращается -1.
namespace ascii { constexpr char tolower(char c); constexpr char toupper(char c); constexpr char upper_to_lower(char upper); constexpr char lower_to_upper(char lower); }
constexpr char tolower(char c)
constexpr char toupper(char c)
Аналоги соответствующих функций из <cctype>.
constexpr char upper_to_lower(char upper)
Более ограниченный аналог tolower(). Результат неопределён, если аргумент не является заглавной буквой ASCII.
Предусловие: ascii::isupper(upper)
constexpr char lower_to_upper(char lower)
Более ограниченный аналог toupper(). Результат неопределён, если аргумент не является строчной буквой ASCII.
Предусловие: ascii::islower(lower)
constexpr bool ascii::equal_icase(char ch1, char ch2);
Проверяет два ASCII-символа на равенство без учета регистра букв.
Инструменты обработки ASCII-строк.
namespace ascii { char *tolower(char *str); std::string &tolower(std::string &str); }
Переводит все заглавные латинские буквы str в строчные. Возвращает str. Указатель на C-строку не должен быть nullptr!
namespace ascii { char *toupper(char *str); std::string &toupper(std::string &str); }
Переводит все строчные латинские буквы str в заглавные. Возвращает str. Указатель на C-строку не должен быть nullptr!
namespace ascii { bool equal_icase(const char *s1, const char *s2); bool equal_icase(const char *s1, size_t s1_len, const char *s2, size_t s2_len); #if __cpp_lib_string_view // C++17 bool equal_icase(std::string_view s1, std::string_view s2); #else // until C++17 bool equal_icase(const std::string &s1, const std::string &s2); bool equal_icase(const std::string &s1, const char *s2); bool equal_icase(const char *s1, const std::string &s2); #endif }
Проверяет две ASCII-строки на равенство без учета регистра букв. Указатели на строки не должны быть nullptr!
Кодировщик и декодировщик Base16.
Класс, используемый как пространство имён. Содержит только типы, константы и статические функции. Создание объектов данного класса не предполагается.
struct base16::bad_format : public std::exception {};
Абстрактный базовый класс исключений.
struct base16::bad_digit : public base16::bad_format { const char *what() const noexcept; };
Класс исключений, бросаемый base16::decode() если входная последовательность содержит символ, не являющийся шестнадцатеричной цифрой.
struct base16::bad_length : public base16::bad_format { const char *what() const noexcept; };
Класс исключений, бросаемый base16::decode() если входная последовательность состоит из нечётного количества символов.
enum class base16::status { ok, invalid_length, invalid_digit }; using base16::status_t = base16::status; // for C++98
Коды статусов разбора входной последовательности, возвращаемые base16::try_decode().
// Bytes -> Text template<class ByteSReader, class CharSWriter> void base16::encode_upper(ByteSReader reader, CharSWriter writer);
Кодирует байты из reader и записывает результирующие символы во writer. Для шестнадцатеричных цифр используется верхний регистр.
ByteSReader должен моделировать concept sreader<unsigned char>. См. S-readers.
CharSWriter должен моделировать concept swriter<char>. См. S-writers.
#include<__vic/sreaders/string.h> #include<__vic/swriters/string.h> using bytes = std::string; using bytes_sreader = __vic::string_sreader; std::string encode_base16(const bytes &s) { std::string res; res.reserve(s.length() * 2); __vic::base16::encode_upper(bytes_sreader(s), __vic::string_swriter(res)); return res; }
// Bytes -> Text template<class ByteSReader, class CharSWriter> void base16::encode_lower(ByteSReader reader, CharSWriter writer);
То же самое, что base16::encode_upper(), но использует нижний регистр для шестнадцатеричных цифр.
// Byte -> Text template<class CharSWriter> void base16::encode_byte_lower(unsigned char byte, CharSWriter writer);
То же самое, что base16::encode_lower(), но кодирует единственный байт.
// Byte -> Text template<class CharSWriter> void base16::encode_byte_upper(unsigned char byte, CharSWriter writer);
То же самое, что base16::encode_upper(), но кодирует единственный байт.
// Text -> Bytes template<class CharSReader, class ByteSWriter> void base16::decode(CharSReader reader, ByteSWriter writer);
Декодирует символы из reader и записывает результирующие байты во writer. Если входная последовательность имеет неправильный Base16 формат, бросается исключение наследник base16::bad_format.
CharSReader должен моделировать concept sreader<char>. См. S-readers.
ByteSWriter должен моделировать concept swriter<unsigned char>. См. S-writers.
#include<__vic/sreaders/string.h> #include<__vic/swriters/string.h> using bytes = std::string; using bytes_swriter = __vic::string_swriter; bytes decode_base16(const std::string &s) { bytes res; res.reserve(s.length() / 2); __vic::base16::decode(__vic::string_sreader(s), bytes_swriter(res)); return res; }
// Text -> Bytes template<class CharSReader, class ByteSWriter> base16::status_t base16::try_decode(CharSReader reader, ByteSWriter writer);
То же самое, что base16::decode(), но возвращает base16::status отличный от base16::status::ok в случае неправильного формата входной последовательности вместо бросания исключений.
Кодировщик и декодировщик Base64.
Класс, используемый как пространство имён. Содержит только типы, константы и статические функции. Создание объектов данного класса не предполагается.
struct base64::bad_format : public std::exception {};
Абстрактный базовый класс исключений.
struct base64::bad_digit : public base64::bad_format { const char *what() const noexcept; };
Класс исключений, бросаемый base64::decode() если входная последовательность содержит символ, не являющийся цифрой Base64.
struct base64::bad_length : public base64::bad_format { const char *what() const noexcept; };
Класс исключений, бросаемый base64::decode() если входная последовательность состоит из количества символов не кратного 4.
enum class base64::status { ok, invalid_length, invalid_digit }; using base64::status_t = base64::status; // for C++98
Коды статусов разбора входной последовательности, возвращаемые base64::try_decode().
// Bytes -> Text template<class ByteSReader, class CharSWriter> void base64::encode(ByteSReader reader, CharSWriter writer);
Кодирует байты из reader и записывает результирующие символы во writer.
ByteSReader должен моделировать concept sreader<unsigned char>. См. S-readers.
CharSWriter должен моделировать concept swriter<char>. См. S-writers.
#include<__vic/sreaders/string.h> #include<__vic/swriters/string.h> using bytes = std::string; using bytes_sreader = __vic::string_sreader; std::string encode_base64(const bytes &s) { std::string res; res.reserve(__vic::base64::encoded_length(s.length())); __vic::base64::encode(bytes_sreader(s), __vic::string_swriter(res)); return res; }
// Text -> Bytes template<class CharSReader, class ByteSWriter> void base64::decode(CharSReader reader, ByteSWriter writer);
Декодирует символы из reader и записывает результирующие байты во writer. Если входная последовательность имеет неправильный Base64 формат, бросается исключение наследник base64::bad_format.
CharSReader должен моделировать concept sreader<char>. См. S-readers.
ByteSWriter должен моделировать concept swriter<unsigned char>. См. S-writers.
#include<__vic/sreaders/string.h> #include<__vic/swriters/string.h> using bytes = std::string; using bytes_swriter = __vic::string_swriter; bytes decode_base64(const std::string &s) { bytes res; res.reserve(__vic::base64::max_decoded_length(s.length())); __vic::base64::decode(__vic::string_sreader(s), bytes_swriter(res)); return res; }
// Text -> Bytes template<class CharSReader, class ByteSWriter> base64::status_t base64::try_decode(CharSReader reader, ByteSWriter writer);
То же самое, что base64::decode(), но возвращает base64::status отличный от base64::status::ok в случае неправильного формата входной последовательности вместо бросания исключений.
constexpr size_t base64::encoded_length(size_t orig_len);
Высчитывает длину закодированной последовательности символов на основе длины входной последовательности в байтах.
constexpr size_t base64::max_decoded_length(size_t orig_len);
Оценивает максимальную длину декодированной последовательности байтов на основе длины входной последовательности символов. Точная длина зависит от символов '=' на конце закодированной последовательности.
class bin_file : private non_copyable { public: enum in_t { in }; enum out_t { out }; enum append_t { append }; bin_file(); bin_file(const char *fname, in_t); bin_file(const char *fname, out_t); bin_file(const char *fname, append_t); ~bin_file(); // BEGIN C++11 bin_file(bin_file &&o) noexcept; bin_file &operator=(bin_file &&o) noexcept; // END C++11 bool open_in(const char *fname); bool open_out(const char *fname); bool open_append(const char *fname); size_t read_max(void *buf, size_t n); size_t read_some(void *buf, size_t n); void write_all(const void *buf, size_t n); bool is_open() const; void close(); bool close_nt() noexcept; void swap(bin_file &o) noexcept; [[noreturn]] void throw_last_error(const char *msg); void throw_if_closed(const char *msg); };
Небуферизированный двоичный файл. Независимая от ОС обёртка низкоуровневого системного API.
Доступны следующие режимы открытия:
enum in_t { in }
enum out_t { out }
enum append_t { append }
Тэги конструкторов.
bin_file()
Постусловие: is_open() == false()
bin_file(const char *fname, in_t)
bin_file(const char *fname, out_t)
bin_file(const char *fname, append_t)
Вызывают open_in(fname), open_out(fname) или open_append(fname), соответственно. is_open() или throw_if_closed() должен быть вызван после для проверки результата.
~bin_file()
Закрывает файл, если is_open() == true.
bin_file(bin_file &&o) noexcept [C++11]
bin_file &operator=(bin_file &&o) noexcept [C++11]
Операции перемещения для режима C++11.
bool open_in(const char *fname)
bool open_out(const char *fname)
bool open_append(const char *fname)
Открыть файл для чтения, записи или добавления, соответственно. Возвращает is_open().
Предусловие: is_open() == false
bool is_open() const
Возвращает true если файл открыт.
size_t read_max(void *buf, size_t n)
Пытается прочитать n байтов в указанный буфер. Возвращает количество байтов, которое удалось прочитать. Возвращённое значение может быть меньше запрошенного только в случае, если достигнут конец файла. При ошибках бросает исключения.
Предусловие: is_open() == true
size_t read_some(void *buf, size_t n)
Читает не более n байтов в указанный буфер. Возвращает количество прочитанных байтов или 0 при достижении конца файлов. Функция сразу возвращает управление, как только прочитан блок байтов любого размера. При ошибках бросает исключения.
Предусловие: is_open() == true
void write_all(const void *buf, size_t n)
Записывает весь переданный буфер в файл. При ошибках бросает исключения.
Предусловие: is_open() == true
void close()
Закрывает открытый файл. При ошибках бросает исключения.
Предусловие: is_open() == trueПостусловие: is_open() == false
bool close_nt() noexcept
Аналог close(), но никогда не бросает исключений. Вместо этого возвращает false при ошибках.
void swap(bin_file &o) noexcept
Обменивается значением с o.
[[noreturn]] void throw_last_error(const char *msg)
Бросает исключение с описанием последней ошибки, если оно доступно. В любом случае what() будет содержать msg в виде подстроки.
void throw_if_closed(const char *msg)
Вызывает throw_last_error(msg), если !is_open().
// Copy file __vic::bin_file in("file", __vic::bin_file::in); in.throw_if_closed("Cannot open file") __vic::bin_file out("file.copy", __vic::bin_file::out); out.throw_if_closed("Cannot create file") char buf[512]; while(size_t n = in.read_some(buf, sizeof buf)) out.write_all(buf, n); out.close(); // in will be closed by destructor
Инструменты для манипуляции битами и байтами.
constexpr uint8_t lo_nibble(uint8_t byte); constexpr uint8_t hi_nibble(uint8_t byte);
Возвращают значение младшего/старшего полубайта (тетрады), соответственно.
template<class T> constexpr T lsb_ones(unsigned bits_num); template<class T> constexpr T msb_ones(unsigned bits_num);
Возвращают значение типа T со всеми младшими/старшими bits_num битами заполненными 1, соответственно. Все остальные биты установлены в 0.
template<class T> constexpr T get_lsbs(T v, unsigned bits_num);
Возвращает bits_num младших битов значения v. Другими словами, заполняет нулями все, кроме младших bits_num битов.
constexpr int ord(char ch);
Возвращает код символа от 0 до 255.
unsigned popcount(unsigned v); unsigned popcount(unsigned long v); unsigned popcount(unsigned long long v); unsigned popcount(unsigned short v); unsigned popcount(unsigned char v);
Возвращает количество единичных битов в данном значении.
unsigned msb_position(unsigned v) unsigned msb_position(unsigned long v); unsigned msb_position(unsigned long long v); unsigned msb_position(unsigned short v); unsigned msb_position(unsigned char v);
Возвращает позицию самого значимого единичного бита. Результат неопределён, если v == 0.
template<class UInt> bool ispow2(UInt n);
Возвращает true, если n является целой степенью 2.
template<class UInt> UInt ceil2(UInt n);
Возвращает минимальное значение m, такое что ispow(m) && m >= n. Если m непредставимо типом UInt, результат неопределён.
template<class UInt> UInt floor2(UInt n);
Если n != 0, возвращает максимальное значение m, такое что ispow2(m) && m <= n. В противном случае возвращается 0.
template<class UInt> unsigned ceil_log2(UInt n);
Возвращает ceil(log2(n)), если n != 0, или 0 в противном случае.
template<class UInt> unsigned floor_log2(UInt n);
Возвращает floor(log2(n)), если n != 0, или 0 в противном случае.
unsigned long long rotl(unsigned long long v, int shift); unsigned long rotl(unsigned long v, int shift); unsigned rotl(unsigned v, int shift); unsigned short rotl(unsigned short v, int shift); unsigned char rotl(unsigned char v, int shift); unsigned long long rotr(unsigned long long v, int shift); unsigned long rotr(unsigned long v, int shift); unsigned rotr(unsigned v, int shift); unsigned short rotr(unsigned short v, int shift); unsigned char rotr(unsigned char v, int shift);
Функции выполняют циклический побитовый сдвиг (rotation) влево (rotl) или вправо (rotr).
constexpr uint8_t swapped_nibbles(uint8_t b);
Меняет местами младший и старший полубайт значения и возвращает его.
template<class T> class bounded_vector : private non_copyable { public: using value_type = T; using iterator = <implementation-defined>; using const_iterator = <implementation-defined>; bounded_vector(); explicit bounded_vector(size_t max_size); ~bounded_vector(); // BEGIN C++11 bounded_vector(bounded_vector &&o) noexcept; bounded_vector &operator=(bounded_vector &&o) noexcept; template<class... Args> T &emplace_back(Args &&... args) // END C++11 // size in objects size_t size() const; size_t capacity() const; bool full() const; bool empty() const; void recreate(size_t new_max_size, bool size_exact = false); void *alloc(); // returns pointer to memory for object allocation void push_allocated(); // adds last allocated object to the container void pop_back(); void clear(); void swap(bounded_vector &o) noexcept; // element access T &operator[](size_t i); iterator begin(); iterator end(); T &front(); T &back(); const T &operator[](size_t i) const; const_iterator begin() const; const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; const T &front() const; const T &back() const; }; template<class T> void swap(bounded_vector<T> &o1, bounded_vector<T> &o2) noexcept;
Стандартные контейнеры в C++98 не позволяют хранить в себе объекты, которые нельзя копировать (недоступны копирующий конструктор и копирующее присваивание). Даже в C++11 элементы контейнера, вроде std::vector, должны быть как минимум noexcept-перемещаемыми. Данный класс решает эту проблему и представляет собой массив некопируемых объектов, или std::vector, capacity() которого не растёт автоматически.
Без использования emplace_back() невозможно создать произвольный новый объект непосредственно в памяти контейнера. В C++98 forwarding ссылки вообще отсутствуют, делая почти невозможным передачу произвольных параметров в конструктор элемента. В bounded_vector это ограничение обходится с использованием следующего механизма. Создание элемента происходит в несколько фаз:
Пример кода смотрите в конце статьи.
При создании задаётся максимальная ёмкость контейнера. Изменить в будущем её можно, но только разрушив содержащиеся в нём объекты, то есть пересоздав контейнер (функция recreate()).
Всегда следует использовать функцию emplace_back() для создания элементов, когда она доступна. Если же нет, то использовать небезопасный интерфейс, описанный выше, с большой осторожностью. Он достаточно страшный и подверженный ошибкам, но выполняет свою функцию. После того, как элемент размещён в контейнере, работать с ним также удобно, как и с любым другим копируемым объектом в стандартных контейнерах. В любом случае, в целом это гораздо удобнее и эффективнее использования альтернатив, таких как создание объектов в куче с последующим хранением в контейнере указателей на них, даже если нам доступен std::unique_ptr для управления временем жизни объектов.
Принципиальные отличия от std::vector:
typename value_type
Тип элементов.
typename iterator
typename const_iterator
Итераторы.
bounded_vector()
Создаёт объект без выделения памяти под элементы.
Постусловие: capacity() == 0
explicit bounded_vector(size_t max_size)
Создаёт контейнер ёмкости max_size элементов.
Постусловие: capacity() == max_size
~bounded_vector()
Вызывает clear().
bounded_vector(bounded_vector &&o) noexcept [C++11]
bounded_vector &operator=(bounded_vector &&o) noexcept [C++11]
Операции перемещения для режима C++11.
size_t size() const
size_t capacity() const
Текущее и максимальное количество элементов в данном экземпляре контейнера.
bool empty() const
Возвращает size() == 0.
bool full() const
Возвращает size() == capacity().
void recreate(size_t new_max_size, bool size_exact = false)
Пересоздаёт контейнер. Сначала вызывается clear(), затем перевыделяется буфер, если new_max_size > capacity() или size_exact == true и new_max_size != capacity().
Постусловие: capacity() >= new_max_size && empty() == true (если size_exact == true, то capacity() == new_max_size && empty() == true)
void *alloc()
Возвращает указатель на блок памяти, достаточный для размещения экземпляра value_type.
Предусловие: !full()Замечание: Используйте emplace_back() в режиме C++11.
void push_allocated()
Фиксирует успешно созданный объект в контейнере, увеличивая size() на единицу.
template<class... Args> T &emplace_back(Args &&... args) [C++11]
Конструирует новый объект в контейнере с помощью переданных аргументов и увеличивает size() на единицу (alloc() + new + push_allocated() одним вызовом). Возвращает ссылку на новый элемент.
Предусловие: !full()
void pop_back()
Удаляет из контейнера последний элемент.
Предусловие: !empty()
void clear()
Разрушает все элементы контейнера в порядке обратном порядку создания.
Постусловие: size() == 0 (empty() == true)
void swap(bounded_vector &o)
template<class T> void swap(bounded_vector<T> &o1, bounded_vector<T> &o2) noexcept
Обменивается значением с o.
T &operator[](size_t i)
const T &operator[](size_t i) const
Доступ к элементам контейнера по индексу.
Предусловие: i < size()
T &front()
const T &front() const
T &back()
const T &back() const
Доступ к первому и последнему элементу контейнера.
Предусловие: !empty()
iterator begin()
const_iterator begin() const
const_iterator cbegin() const
iterator end()
const_iterator end() const
const_iterator cend() const
Доступ к элементам через итераторы.
// Создаём вектор на два объекта класса C __vic::bounded_vector<C> v(2); // Создание элемента в режиме C++98: new(v.alloc()) C(...); // Запрашиваем блок памяти и создаём в нём объект v.push_allocated(); // Фиксируем в контейнере успешно созданный объект // Создание элемента в режиме C++11: v.emplace_back(...);
Работа с датой и временем.
constexpr bool is_leap_year(int year);
Определяет, является ли данный год високосным, согласно Григорианскому календарю.
int days_in_month(int month, int year);
Возвращает количество дней в данном месяце. Месяц - это число от 1 до 12. Второй параметр используется только, если месяц - 2 (февраль), в остальных случаях просто игнорируется.
long days_between_years(unsigned year1, unsigned year2);
Возвращает разность в днях между началами годов year2 и year1.
class invalid_date; // : public std::exception
Исключение, бросаемое в случае неправильного элемента даты или времени.
void validate_date(int yy, int mm, int dd); void validate_time(int hh, int mi, int ss); void validate_date_time(int yy, int mm, int dd, int hh, int mi, int ss);
Проверяет правильность даты/времени. Проверяются следующие ограничения:
В случае нарушения какого-то из ограничений бросается invalid_date.
Утилиты для манипуляций порядком байт.
namespace endian {
enum endianness
{
unknown = 0,
little = 1234,
big = 4321,
pdp = 3412,
native = <one-of-the-above>
};
} // namespace
using endian::endianness;
Константы, соответсвующие различным порядкам байт. endian::native принимает значение одной из констант, в зависимости от текущей платформы (подобно макросу __BYTE_ORDER__ в UNIX-подобных системах). Предполагается использование данных констант для специализации шаблонов или проверках времени компиляции (например с использованием static_assert).
template<__vic::endianness > struct some_algo; // not implemented // Реализация для little-endian template<> struct some_algo<__vic::endian::little> { static void doit() { ... } }; // Реализация для big-endian template<> struct some_algo<__vic::endian::big> { static void doit() { ... } }; // Автоматически выбрать подходящую для используемой платформы реализацию some_algo<__vic::endian::native>::doit();
static_assert(__vic::endian::native == __vic::endian:little, "Litte-endian is expected");
template<class T> [[nodiscard]] constexpr T endian::from_little(T v); template<class T> [[nodiscard]] constexpr T endian::from_big(T v);
Возвращают значение, представленное в порядке байт, используемом платформой, преобразованное из litte/big endian, если необходимо.
T может быть любым интегральным типом или enum размера не более, чем sizeof(long long).
uint16_t v; read_bytes(&v, 2); // serialized as big endian std::cout << "The value is " << __vic::endian::from_big(v) << '\n';
template<class T> [[nodiscard]] constexpr T endian::to_little(T v); template<class T> [[nodiscard]] constexpr T endian::to_big(T v);
Возвращают значение, представленное в litte/big endian.
T может быть любым интегральным типом или enum размера не более, чем sizeof(long long).
uint16_t v = __vic::endian::to_big(...); write_bytes(&v, 2); // serialize as big endian
[[nodiscard]] constexpr uint16_t swab16(uint16_t v); [[nodiscard]] constexpr uint32_t swab32(uint32_t v); [[nodiscard]] constexpr uint64_t swab64(uint64_t v);
Быстрые утилиты для обращения порядка байт (обычно реализуются с помощью специфичных для компилятора intrinsics).
static_assert(__vic::swab32(0x01020304) == 0x04030201);
Инструменты обработки ошибок.
class exception : public std::exception { public: exception(); explicit exception(const char *message); const char *what() const noexcept; protected: void set_message(const char *message); };
Небольшое расширение std::exception - объект хранит сообщение об ошибке, переданное в конструкторе, которое потом возвращает what(). Может быть использован как базовый или конкретный класс исключения. Не использует и не зависит от std::string, в отличие от std::logic_error и std::runtime_error. Также Вам нет нужды решать, какое из последних следует использовать в каждом конкретном случае.
exception()
Создаёт объект с пустым сообщением об ошибке.
explicit exception(const char *message)
Создаёт объект с указанным сообщением об ошибке.
const char *what() const noexcept
Возвращает установленное ранее сообщение.
void set_message(const char *message)
Устанавливает новое сообщение.
struct custom_exception : public __vic::exception { explicit custom_exception(const char *msg) : __vic::exception(msg) {} }; throw custom_exception("Error condition description");
class libc_error : public std::exception { public: explicit libc_error(int err_no = errno); explicit libc_error(const char *prompt, int err_no = errno); const char *what() const noexcept; int code() const; int get_errno() const; };
Класс предназначен для замены стандартного механизма обработки ошибок в мире C - errno - на механизм исключений. Также класс пригоден для использования в многопоточных приложениях вместо не всегда реентерабельного вызова std::strerror().
Ниже приведён фрагмент кода, типичный для языка C:
// C: int fd; if((fd = open("qqqq", O_RDONLY)) == -1) { perror("open"); if(errno == ENOENT) exit(1); }
Если файл не найден, программа напечатает
open: No such file or directory
в stderr и выйдет, вернув в ОС значение 1.
Какие проблемы присущи этому коду? Во-первых, не у каждой программы есть stderr, поэтому библиотечная функция не может сама выводить туда сообщения об ошибках. Во-вторых, значение глобальной переменной errno может быть затёрто любым следующим вызовом, если его не сохранить. В-третьих, решение о завершении процесса должна принимать конечная программа. Обычная библиотечная функция не может брать на себя такие полномочия. В-четвёртых, в общем случае программа на С++ не может вызывать std::exit(), так как не будут вызваны деструкторы активных объектов, что может разрушить логику работы программы.
Ниже приведён адаптированный пример для C++ с использованием описываемого класса:
// C++: try { int fd = open("qqqq", O_RDONLY); if(fd == -1) throw __vic::libc_error("open"); // or just // if(fd == -1) throw __vic::libc_error(); } catch(const __vic::libc_error &ex) { std::cerr << ex.what() << '\n'; if(ex.code() == ENOENT) return 1; }
Как видно, функция просто корректно отслеживает ошибочную ситуацию и сообщает о ней вызывающей среде. Далее вызывающая сторона уже сама может принять решение об обработке ошибки. В простейшем случае она поступает также как предыдущая C-программа: печатает сообщение в стандартный поток вывода ошибок и завершает выполнение. Кроме того, код ошибки надёжно сохранён в исключении и ни кем не перетрётся.
explicit libc_error(int err_no = errno)
err_no - код ошибки.
Постусловие: code() == err_no
explicit libc_error(const char *prompt, int err_no = errno)
prompt - заголовок выводимого сообщения. Параметр имеет такой же смысл, как и параметр std::perror().
const char *what() const noexcept
Возвращает описание ошибки в формате std::perror().
int code() const
int get_errno() const
Возвращает хранимый код ошибки.
Утилиты для работы с файловой системой.
bool path_exists(const char *path); bool path_exists(const std::string &path); bool file_exists(const char *path); bool file_exists(const std::string &path); bool dir_exists(const char *path); bool dir_exists(const std::string &path);
path_exists() проверяет, существует ли путь в фаловой системе. Вторая и третья функции дополнительно, кроме существования, проверяют, ссылается ли путь на регулярный файл или на каталог, соответсвенно.
void mkdir(const char *path, mode_t mode = 0777); void mkdir(const std::string &path, mode_t mode = 0777); bool mkdir_if_absent(const char *path, mode_t mode = 0777); bool mkdir_if_absent(const std::string &path, mode_t mode = 0777);
Создаёт каталог. Бросает исключения в случае ошибок. mkdir_if_absent() возвращает false вместо исключения, если каталог с таким именем уже существует.
void rmdir(const char *path); void rmdir(const std::string &path); bool rmdir_if_exists(const char *path); bool rmdir_if_exists(const std::string &path);
Удаляет пустой каталог. Бросает исключения в случае ошибок. rmdir_if_exists() возвращает false вместо исключения, если каталога с таким именем не существует.
std::string get_current_dir();
Возвращает текущий рабочий каталог.
void remove_file(const char *path); void remove_file(const std::string &path); bool remove_file_if_exists(const char *path); bool remove_file_if_exists(const std::string &path); bool remove_file_nt(const char *path) noexcept; bool remove_file_nt(const std::string &path) noexcept;
Удаляет файл. Бросает исключения в случае ошибок.
remove_file_if_exists() возвращает false вместо исключения, если файл с таким именем не существует.
remove_file_nt() вообще не бросает исключений, а возвращает false в случае ошибок.
void copy_file( const char *src_path, const char *dest_path, bool replace = false); void copy_file( const std::string &src_path, const std::string &dest_path, bool replace = false); bool copy_file_if_exists( const char *src_path, const char *dest_path, bool replace = false); bool copy_file_if_exists( const std::string &src_path, const std::string &dest_path, bool replace = false); void copy_file_replace( const char *src_path, const char *dest_path); void copy_file_replace( const std::string &src_path, const std::string &dest_path); bool copy_file_replace_if_exists( const char *src_path, const char *dest_path); bool copy_file_replace_if_exists( const std::string &src_path, const std::string &dest_path);
Создаёт новый файл dest_path, являющийся копией файла src_path. Вызов заканчивается ошибкой, если новый файл существует и replace == false.
copy_file_if_exists() возвращает false вместо исключения, если файла с именем src_path не существует.
copy_file_replace() - то же самое, что copy_file(..., true).
copy_file_replace_if_exists() - то же самое, что copy_file_if_exists(..., true).
void move_file(const char *src_path, const char *dest_path); void move_file(const std::string &src_path, const std::string &dest_path); bool move_file_if_exists(const char *src_path, const char *dest_path); bool move_file_if_exists( const std::string &src_path, const std::string &dest_path); void move_file_replace(const char *src_path, const char *dest_path); void move_file_replace( const std::string &src_path, const std::string &dest_path); bool move_file_replace_if_exists( const char *src_path, const char *dest_path); bool move_file_replace_if_exists( const std::string &src_path, const std::string &dest_path);
Перемещает файл src_path в новое место, указанное в dest_path.
Функции с суффиксом _replace перетирают целевой файл, если он существует, остальные - возвращают ошибку в таком случае.
move_file_if_exists() возвращает false вместо ошибки, если файл с именем src_path не существует.
void rename_file(const char *src_name, const char *dest_name); void rename_file(const std::string &src_name, const std::string &dest_name); bool rename_file_if_exists(const char *src_name, const char *dest_name); bool rename_file_if_exists( const std::string &src_name, const std::string &dest_name); void rename_file_replace(const char *src_name, const char *dest_name); void rename_file_replace( const std::string &src_name, const std::string &dest_name); bool rename_file_replace_if_exists( const char *src_name, const char *dest_name); bool rename_file_replace_if_exists( const std::string &src_name, const std::string &dest_name);
Переименовывает файл src_path в dest_path. Новый путь должен находиться на той же самой физической файловой системе.
В отличие от std::rename(), вызов функций без суффикса _replace заканчивается ошибкой, если файл с именем dest_path уже существует.
rename_file_if_exists() возвращает false вместо исключения, если файла с именем src_path не существует.
uintmax_t file_size(const char *path); uintmax_t file_size(const std::string &path);
Возвращает размер файла в байтах.
class ipv4_addr : public ::in_addr { public: ipv4_addr() = default; constexpr ipv4_addr(::in_addr a); constexpr explicit ipv4_addr(::in_addr_t a); constexpr ipv4_addr(uint8_t a, uint8_t b, uint8_t c, uint8_t d); static constexpr ipv4_addr any(); static constexpr ipv4_addr loopback(); constexpr bool is_any() const; constexpr bool is_loopback() const; static bool parse(const char *begin, const char *end, ::in_addr &res); #if __cpp_lib_string_view // C++17 static bool parse(std::string_view s, ::in_addr &res); #else // until C++17 static bool parse(const std::string &s, ::in_addr &res) static bool parse(const char *s, ::in_addr &res); #endif }; constexpr ipv4_addr operator&(ipv4_addr a1, ipv4_addr a2); constexpr bool operator==(ipv4_addr a1, ipv4_addr a2); constexpr bool operator!=(ipv4_addr a1, ipv4_addr a2); constexpr bool operator< (ipv4_addr a1, ipv4_addr a2); constexpr bool operator> (ipv4_addr a1, ipv4_addr a2); constexpr bool operator<=(ipv4_addr a1, ipv4_addr a2); constexpr bool operator>=(ipv4_addr a1, ipv4_addr a2); constexpr bool operator==(ipv4_addr a1, ::in_addr a2); constexpr bool operator!=(ipv4_addr a1, ::in_addr a2); constexpr bool operator< (ipv4_addr a1, ::in_addr a2); constexpr bool operator> (ipv4_addr a1, ::in_addr a2); constexpr bool operator<=(ipv4_addr a1, ::in_addr a2); constexpr bool operator>=(ipv4_addr a1, ::in_addr a2); constexpr bool operator==(::in_addr a1, ipv4_addr a2); constexpr bool operator!=(::in_addr a1, ipv4_addr a2); constexpr bool operator< (::in_addr a1, ipv4_addr a2); constexpr bool operator> (::in_addr a1, ipv4_addr a2); constexpr bool operator<=(::in_addr a1, ipv4_addr a2); constexpr bool operator>=(::in_addr a1, ipv4_addr a2); void to_text_append(::in_addr ip, std::string &res); template<> struct std::hash<ipv4_addr>;
IPv4 адрес. Удобная C++-обёртка для ::in_addr.
ipv4_addr() = default
Создаёт неинициализированный IPv4 адрес.
constexpr ipv4_addr(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
Создаёт IPv4 адрес a.b.c.d.
constexpr ipv4_addr(::in_addr a)
constexpr explicit ipv4_addr(::in_addr_t a)
Конвертеры из системных типов.
static constexpr ipv4_addr any()
Возвращает IPv4 адрес 0.0.0.0.
static constexpr ipv4_addr loopback()
Возвращает IPv4 адрес 127.0.0.1.
constexpr bool is_any() const
Возвращает true, если адрес - 0.0.0.0.
constexpr bool is_loopback() const
Возвращает true, если адрес - 127.0.0.1.
static bool parse(const char *begin, const char *end, ::in_addr &res)
Конвертация IPv4 адреса из текстового представления. В случае успеха возвращается true, и res содержит сконвертированное значение.
Замечание: Ожидаемый входной формат - строго 4 компоненты в десятичном представлении, разделённые точками.
static bool parse(std::string_view s, ::in_addr &res) [C++17]
Альтернативный прототип одноимённой функции для C++17.
static bool parse(const std::string &s, ::in_addr &res) [until C++17]
static bool parse(const char *s, ::in_addr &res) [until C++17]
Альтернативные прототипы одноимённой функции до C++17.
constexpr ipv4_addr operator&(ipv4_addr a1, ipv4_addr a2)
Применяет маску к адресу.
constexpr bool operator==(ipv4_addr a1, ipv4_addr a2)
constexpr bool operator!=(ipv4_addr a1, ipv4_addr a2)
constexpr bool operator==(ipv4_addr a1, ::in_addr a2)
constexpr bool operator!=(ipv4_addr a1, ::in_addr a2)
constexpr bool operator==(::in_addr a1, ipv4_addr a2)
constexpr bool operator!=(::in_addr a1, ipv4_addr a2)
Операции проверки на равенство.
constexpr bool operator< (ipv4_addr a1, ipv4_addr a2)
constexpr bool operator> (ipv4_addr a1, ipv4_addr a2)
constexpr bool operator<=(ipv4_addr a1, ipv4_addr a2)
constexpr bool operator>=(ipv4_addr a1, ipv4_addr a2)
constexpr bool operator< (ipv4_addr a1, ::in_addr a2)
constexpr bool operator> (ipv4_addr a1, ::in_addr a2)
constexpr bool operator<=(ipv4_addr a1, ::in_addr a2)
constexpr bool operator>=(ipv4_addr a1, ::in_addr a2)
constexpr bool operator< (::in_addr a1, ipv4_addr a2)
constexpr bool operator> (::in_addr a1, ipv4_addr a2)
constexpr bool operator<=(::in_addr a1, ipv4_addr a2)
constexpr bool operator>=(::in_addr a1, ipv4_addr a2)
Операции сравнения. Конкретный порядок может отличаться на разных платформах, но в пределах одной платформы является математически согласованным.
void to_text_append(::in_addr ip, std::string &res)
Преобразуют IPv4 адрес в десятичное точечное текстовое представление.
template<> struct std::hash<ipv4_addr>
Специализация std::hash.
http_connect(__vic::ipv4_addr{127,0,0,1}, 80); std::string_view s = ..; __vic::ipv4_addr ip; if(__vic::ipv4_addr::parse(s, ip)) log.info() << "Connecting to " << ip << "..."; else log.error("Bad IPv4 address format");
class ipv6_addr : public ::in6_addr { public: ipv6_addr() = default; constexpr ipv6_addr(::in6_addr a); constexpr ipv6_addr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h, uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p); constexpr ipv6_addr(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h) explicit ipv6_addr(const ::in_addr &ip4); static constexpr ipv6_addr any(); static constexpr ipv6_addr loopback(); bool is_any() const; bool is_loopback() const; bool is_ipv4_mapped() const; ipv4_addr to_ipv4() const; static bool parse(const char *begin, const char *end, ::in6_addr &res); #if __cpp_lib_string_view static bool parse(std::string_view s, ::in6_addr &res); #else static bool parse(const std::string &s, ::in6_addr &res); static bool parse(const char *s, ::in6_addr &res); #endif }; ipv6_addr operator&(const ipv6_addr &a1, const ipv6_addr &a2); constexpr bool operator==(ipv6_addr a1, ipv6_addr a2); constexpr bool operator!=(ipv6_addr a1, ipv6_addr a2); constexpr bool operator< (ipv6_addr a1, ipv6_addr a2); constexpr bool operator> (ipv6_addr a1, ipv6_addr a2); constexpr bool operator<=(ipv6_addr a1, ipv6_addr a2); constexpr bool operator>=(ipv6_addr a1, ipv6_addr a2); constexpr bool operator==(ipv6_addr a1, ::in6_addr a2); constexpr bool operator!=(ipv6_addr a1, ::in6_addr a2); constexpr bool operator< (ipv6_addr a1, ::in6_addr a2); constexpr bool operator> (ipv6_addr a1, ::in6_addr a2); constexpr bool operator<=(ipv6_addr a1, ::in6_addr a2); constexpr bool operator>=(ipv6_addr a1, ::in6_addr a2); constexpr bool operator==(::in6_addr a1, ipv6_addr a2); constexpr bool operator!=(::in6_addr a1, ipv6_addr a2); constexpr bool operator< (::in6_addr a1, ipv6_addr a2); constexpr bool operator> (::in6_addr a1, ipv6_addr a2); constexpr bool operator<=(::in6_addr a1, ipv6_addr a2); constexpr bool operator>=(::in6_addr a1, ipv6_addr a2); void to_text_append(::in6_addr ip, std::string &res); string_buffer &operator<<(string_buffer &s, ::in6_addr ip); template<> struct std::hash<ipv6_addr>;
Аналог ipv4_addr для IPv6/::in6_addr.
explicit ipv6_addr(const ::in_addr &ip4)
Создаёт IPv4-mapped IPv6 address.
Постусловие: is_ipv4_mapped() == true
static constexpr ipv6_addr any()
Возвращает IPv6 адрес ::.
static constexpr ipv6_addr loopback()
Возвращает IPv6 адрес ::1.
bool is_any() const
Возвращает true, если адрес - ::.
bool is_loopback() const
Возвращает true, если адрес - ::1.
bool is_ipv4_mapped() const
Возвращает true, если значение является IPv4-mapped IPv6 address.
ipv4_addr to_ipv4() const
Конвертирует IPv4-mapped IPv6 address в ipv4_addr.
Предусловие: is_ipv4_mapped() == true
Инструменты работы с итераторами.
template<class T, size_t N> constexpr T *begin(T (&arr)[N]); template<class T, size_t N> constexpr T *end(T (&arr)[N]); template<class T, size_t N> constexpr const T *cbegin(T (&arr)[N]); template<class T, size_t N> constexpr const T *cend(T (&arr)[N]);
Возвращают указатели на начало и за конец массива.
int vals[] = { 1, 2, 3 }; std::list<int> lst(__vic::begin(vals), __vic::end(vals)); assert(lst.size() == 3);
template<class Iter> void advance(Iter &it, Iter end, size_t n);
Функция похожа на std::advance(), но отличается по формату вызова и поведению. Главные отличия:
template<class ForwardIterator> ForwardIterator next(ForwardIterator it); template<class ForwardIterator> ForwardIterator next(ForwardIterator it, size_t n); template<class BidirectionalIterator> BidirectionalIterator prev(BidirectionalIterator it); template<class BidirectionalIterator> BidirectionalIterator prev(BidirectionalIterator it, size_t n);
next() возвращает значение итератора, передвинутое вперёд на n позиций. prev() делает то же самое, но в противоположную сторону. В отличие от одноимённых функций из STL C++11, смещение не может быть отрицательным. Версии с одним параметром просто вызывают ++it/--it и возвращают результат.
template<class Container> void f(const Container &c) { // Начать обход со второго элемента // v.begin() + 1 работает только для RandomAccessIterator // ++v.begin() может вообще не скомпилироваться for(auto it = __vic::next(c.begin()); it != c.end(); ++it) ...; }
class logger : private non_copyable { public: enum class severity : unsigned char { trace, debug, info, notice, warning, error, fatal }; using severity_t = severity; // use this alias as a type name struct output { virtual void publish_record(severity_t , const char * , size_t ) = 0; protected: ~output() = default; }; class settings_t { struct output &output() const; severity_t level() const; }; class record; explicit logger(output &out, severity_t = severity::info); explicit logger(settings_t s); ~logger(); severity_t level() const; void level(severity_t new_level); settings_t settings() const; output &reset_output(output &out); output &get_output(); const output &get_output() const; static constexpr size_t min_buffer_size = ...; void shrink_buffer(size_t limit); void message(severity_t severity, const char *msg, size_t msg_len); #if __cpp_lib_string_view // C++17 void message(severity_t severity, std::string_view msg); void trace(std::string_view msg); void debug(std::string_view msg); void info(std::string_view msg); void notice(std::string_view msg); void warning(std::string_view msg); void error(std::string_view msg); void fatal(std::string_view msg); #else // until C++17 void message(severity_t severity, const char *msg); void message(severity_t severity, const std::string &msg); void trace(const char *msg); void debug(const char *msg); void info(const char *msg); void notice(const char *msg); void warning(const char *msg); void error(const char *msg); void fatal(const char *msg); void trace(const std::string &msg); void debug(const std::string &msg); void info(const std::string &msg); void notice(const std::string &msg); void warning(const std::string &msg); void error(const std::string &msg); void fatal(const std::string &msg); #endif #if __cpp_lib_format >= 202106L // C++20 + P2508 template<class... Args> void format(severity_t s, std::format_string<Args...> fmt, Args&&... args); template<class Arg1, class... Args> void trace(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); template<class Arg1, class... Args> void debug(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); template<class Arg1, class... Args> void info(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); template<class Arg1, class... Args> void notice(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); template<class Arg1, class... Args> void warning(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); template<class Arg1, class... Args> void error(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); template<class Arg1, class... Args> void fatal(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args); #endif record trace(); record debug(); record info(); record notice(); record warning(); record error(); record fatal(); bool trace_visible() const; bool debug_visible() const; bool info_visible() const; bool notice_visible() const; bool warning_visible() const; bool error_visible() const; bool fatal_visible() const; }; class logger::record { public: record(logger &log, severity_t sev); ~record(); record append(const char *str, size_t str_len); template<class T> record operator<<(const T &v); }; template<class T> struct log_value { static void to_text(const T &v, std::string &s); }; const char *to_string(logger::severity_t s); #if __cpp_lib_string_view // C++17 constexpr std::string_view to_string_view(logger::severity s); #endif
Front-end логгера. Реализует построение записей с помощью оператора <<, подобно стандартной библиотеке потоков iostream. Каждая запись лога имеет назначенный приоритет. Логгер может фильтровать вывод записей по этому приоритету. Предопределены 6 уровней приоритета (в порядке возрастания):
INFO является уровнем логирования по умолчанию. Если сообщение (запись) имеет приоритет ниже текущего уровня логирования, то оно игнорируется и никуда не выводится.
Для создания сообщений с нужным приоритетом предоставляется набор одноимённых функций. Например, info() для сообщений INFO. Также есть универсальная функция message(), в которой приоритет задаётся параметром, но, обычно, следует использовать специфичные функции.
Существует 2 способа создания сообщений. Первый прост и обычен:
log.trace("Trace message"); log.debug("Debug message"); log.info("Info message"); log.notice("Notice"); log.warning("Warning"); log.error("Recoverable error"); log.fatal("Fatal error");
Второй немного более сложен, но предоставляет гораздо большие возможности:
log.error() << "Cannot open file " << filename << '!'; log.warning() << "Loop iteration no " << i;
Вызов функции без параметров создаёт объект типа logger::record с соответствующим приоритетом. Теперь в него можно писать сообщение с использованием оператора <<. Сформированная запись будет выведена в лог по завершении вычисления «полного выражения» (термин из Стандарта)
Если сформировать запись лога сложно или невозможно одним выражением, то следует явно создать объект logger::record и писать в него. Запись будет выведена с лог при вызове деструктора данного объекта:
{ logger::record rec = log.info(); // Начало формирования записи rec << "List elements: "; for(auto el : list) rec << el << ", "; // Сформированная запись попадёт в лог при выходе из блока }
log.info("Message"); // а не log.info() << "Message";
Вывод записей с приоритетами DEBUG и TRACE обычно отключен в нормальных условиях. Такие записи хоть и не попадут в лог, но время на их форматирование всё равно будет тратиться. Поэтому перед попыткой сформировать какую-нибудь отладочную запись с помощью операторов << проверьте, включена ли отладка, с помощью вызова debug_visible() или trace_visible():
if(log.debug_visible()) log.debug() << ...; // формируем запись
Это не относится к обычным вызовам debug(msg) и trace(msg), которым передаётся уже готовая к выводу строка, и никакого дополнительного форматирования не требуется.
Для использования logger нужно реализовать абстрактный базовый класс logger::output (определить publish_record()). Реализация должна вывести куда-то сформированную запись, например, в файл или БД. output, переданный в logger при конструировании, впоследствии можно заменить с помощью вызова reset_output().
severity::trace
severity::debug
severity::info
severity::notice
severity::warning
severity::error
severity::fatal
Константы приоритетов и уровней логирования. Данная форма используется как в режиме C++11, так и в C++98.
typename severity_t
Используйте данный идентификатор, если коду требуется совместимость с режимом C++98. Начиная с C++11 это просто синоним severity.
class output
Интерфейс back-end'а логирования.
void output::publish_record(severity_t sev, const char *buf, size_t buf_len)
Реализация этой чисто виртуальной функции должна вывести содержимое buf длиной buf_len в лог как одну запись. Функция вызывается только когда переданный sev >= level(). Реализация может полагаться на это предусловие.
class settings_t
Хранит настройки логгера: уровень логирования и ссылку на вывод (level() + get_output()).
explicit logger(output &out, severity_t level = severity::info)
Создаёт логгер с данным выводом и уровнем логирования. Время жизни объекта, на который ссылается out, должно превосходить время жизни логгера!
Постусловие: this->level() == level && &this->get_output() == &out
explicit logger(settings_t s)
Создаёт логгер с указанными настройками.
severity_t level() const
Возвращает текущий уровень логирования.
void level(severity_t new_level)
Устанавливает уровень логирования.
Постусловие: level() == new_level
settings_t settings() const
Возвращает текущие настройки.
output &reset_output(output &out)
Устанавливает новый вывод и возвращает старый.
Постусловие: &get_output() == &out
output &get_output()
const output &get_output() const
Возвращает ссылку на текущий вывод.
static constexpr size_t min_buffer_size
Минимальный размер внутреннего буфера в байтах.
void shrink_buffer(size_t limit)
Устанавливает размер внутреннего буфера в min_buffer_size, если он превосходит limit байтов. Позволяет предотвратить бесконрольное разрастание используемой памяти при выводе записей большого размера.
void message(severity_t severity, const char *msg, size_t msg_len)
void message(severity_t severity, std::string_view msg) [C++17]
void message(severity_t severity, const char *msg) [until C++17]
void message(severity_t severity, const std::string &msg) [until C++17]
Выводит сообщение с заданным приоритетом.
void trace(std::string_view msg) [C++17]
void trace(const char *msg) [until C++17]
void trace(const std::string &msg) [until C++17]
void debug(std::string_view msg) [C++17]
void debug(const char *msg) [until C++17]
void debug(const std::string &msg) [until C++17]
void info(std::string_view msg) [C++17]
void info(const char *msg) [until C++17]
void info(const std::string &msg) [until C++17]
void notice(std::string_view msg) [C++17]
void notice(const char *msg) [until C++17]
void notice(const std::string &msg) [until C++17]
void warning(std::string_view msg) [C++17]
void warning(const char *msg) [until C++17]
void warning(const std::string &msg) [until C++17]
void error(std::string_view msg) [C++17]
void error(const char *msg) [until C++17]
void error(const std::string &msg) [until C++17]
void fatal(std::string_view msg) [C++17]
void fatal(const char *msg) [until C++17]
void fatal(const std::string &msg) [until C++17]
Выводит сообщение с соответствующим приоритетом.
template<class... Args>
void format(severity_t s,
std::format_string<Args...> fmt, Args&&... args) [C++20]
Форматирует сообщение используя указанную строку формата и аргументы (подобно std::format), затем выводит его с заданным приоритетом.
template<class Arg1, class... Args>
void trace(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
template<class Arg1, class... Args>
void debug(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
template<class Arg1, class... Args>
void info(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
template<class Arg1, class... Args>
void notice(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
template<class Arg1, class... Args>
void warning(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
template<class Arg1, class... Args>
void error(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
template<class Arg1, class... Args>
void fatal(std::format_string<Arg1,Args...> fmt, Arg1 &&arg1, Args&&... args) [C++20]
Форматирует сообщение используя указанную строку формата и аргументы (подобно std::format), затем выводит его с соответствующим приоритетом.
logger::record trace()
logger::record debug()
logger::record info()
logger::record notice()
logger::record warning()
logger::record error()
logger::record fatal()
Создаёт новую запись c соответствующим приоритетом и позволяет писать в неё части сообщения с помощью оператора <<.
bool trace_visible() const
bool debug_visible() const
bool info_visible() const
bool notice_visible() const
bool warning_visible() const
bool error_visible() const
bool fatal_visible() const
Возвращает true, если запись с указанным уровнем логирования будет выведена в лог при текущих настройках. Использование данных функций позволяет исключить форматирование сообщений, которые всё равно не попадут в лог.
record::record(logger &log, severity_t sev)
Создаёт запись лога с указанным приоритетом. Обычно вместо явного вызова данного конструктора следует использовать функции logger’а, вроде info() без параметров, для создания объекта.
record::~record()
Выводит сформированную запись в лог.
record record::append(const char *str, size_t str_len)
Добавляет строку к сообщению.
template<class T> record record::operator<<(const T &v)
Набор инсертеров для различных типов данных. Указанное значение преобразуется в текст с использованием вызова log_value<T>::to_text().
template<class T> void log_value<T>::to_text(const T &v, std::string &s)
Преобразует значение типа T в текст, используя unqualified вызов to_text_append().
Замечание: Данная функция (точнее класс со статической функцией) может быть спецализирована для Вашего T. Однако, обычно, Вам следует просто определить to_text_append(const T &, std::string &).
const char *to_string(logger::severity_t s)
constexpr std::string_view to_string_view(logger::severity s) [C++17]
Преобразует приоритет записи лога в текстовое представление, которое можно, например, вывести в лог. Пример: для severity::debug возвращается "DEBUG" и т.п.
///////////////////////////////////////////////////////////////////////////// // Вывод сообщений в std::clog с указанием приоритета class coutput : public __vic::logger::output { public: void publish_record(__vic::logger::severity_t s, const char *rec, size_t rec_n) { std::clog << to_string(s) << ": "; std::clog.write(rec, rec_n) << std::endl; } }; ///////////////////////////////////////////////////////////////////////////// int main() { coutput log_output: __vic::logger log(log_output, __vic::logger::severity::debug); log.info("Application is started"); for(int i = 0; i < 5; i++) log.debug() << "Loop i = " << i; log.warning("Application end"); }
Результат:
INFO: Application is started DEBUG: Loop i = 0 DEBUG: Loop i = 1 DEBUG: Loop i = 2 DEBUG: Loop i = 3 DEBUG: Loop i = 4 WARNING: Application end
Утилиты для работы с памятью.
template<class T> T load_unaligned(const void *p);
Загружает значение с потенциально невыровненного адреса, не вызывая ошибку шины (SIGBUS).
const void *p = ...; // int data = *static_cast<const int *>(p); // потенциальная ошибка шины int data = __vic::load_unaligned<int>(p);
template<class T> void store_unaligned(void *p, T v);
Записывает значение по потенциально невыровненному адресу, не вызывая ошибку шины (SIGBUS).
void *p = ...; // *static_cast<int *>(p) = 123; // потенциальная ошибка шины __vic::store_unaligned(p, 123);
class mutex : private non_copyable { public: mutex(); ~mutex(); void lock(); bool try_lock(); bool unlock() noexcept; };
Обычный нерекурсивный мьютекс.
В большинстве случаев, явного использования lock() и unlock() нужно избегать. Вместо этого используйте класс mutex_lock для управления блокировкими. Он обеспечивает безопасность при исключениях и удобен для использования.
В режиме C++11 std::mutex может быть более подходящим вариантом.
mutex()
Создаёт незаблокированный мьютекс.
~mutex()
Уничтожает мьютекс.
void lock()
Захватывает блокировку на мьютексе. Ждёт его освобождения, если он уже захвачен другим потоком в данный момент.
bool try_lock()
Пытается захватить мьютекс. Немедленно возвращает false без ожидания, если он уже захвачен другим потоком.
bool unlock() noexcept
Освобождает ранее захваченный мьютекс. Иногда может возвращать false в случае ошибок, но в общем случае обнаружение ошибок не гарантируется.
См. mutex_lock.
class mutex_lock : private non_copyable { public: enum adopt_t { adopt }; explicit mutex_lock(mutex &mtx); mutex_lock(mutex &mtx, adopt_t); ~mutex_lock(); };
Управляет блокировкой на мьютексе. Снимает блокировку по окончании времени жизни объекта.
adopt
Тэг конструктора, подавляет захват мьютекса.
explicit mutex_lock(mutex &mtx)
Захватывает mtx.
~mutex_lock()
Освобождает mtx.
mutex_lock(mutex &mtx, adopt_t)
Принимает уже захваченный mtx. См. пример.
// Типичное использование __vic::mutex mtx; void reentrant_function() { __vic::mutex_lock lock(mtx); // Критическая секция до конца этого блока ... } // Использование незахватывающего конструктора if(mtx.try_lock()) // Пытаемся захватить мьютекс { // Мьютекс успешно захвачен __vic::mutex_lock lock(mtx, __vic::mutex_lock::adopt); // Критическая секция до конца этого блока ... } else { // Мьютекс удерживается другим потоком ... }
Включение первого файла отключает выравнивание полей структур. Другими словами, включает «упаковку структур» - размер структуры - это строго сумма размеров его полей. Включение второго файла восстанавливает выравнивание, используемое по умолчанию. Таким образом, пара директив #include формируют секцию исходного файла с отключенным выравниванием сруктур.
struct unpacked { bool f1; int f2; }; static_assert(sizeof(unpacked) >= sizeof(bool) + sizeof(int), "Total struct size can exceed the sum of members sizes"); #include<__vic/packon.h> // выравнивание отключено, начиная отсюда struct packed { bool f1; int f2; }; static_assert(sizeof(packed) == sizeof(bool) + sizeof(int), "Total struct size is exactly the sum of members sizes"); #include<__vic/packoff.h> // выравнивание снова включено
class readonly_cstring { public: readonly_cstring(); readonly_cstring(const char *str); readonly_cstring(const char *begin, const char *end); readonly_cstring(const char *chars, size_t n); readonly_cstring(const readonly_cstring &str); ~readonly_cstring() noexcept; // BEGIN C++11 readonly_cstring(readonly_cstring &&str) noexcept; readonly_cstring &operator=(readonly_cstring &&str) noexcept; // END C++11 readonly_cstring &operator=(const char *str); readonly_cstring &operator=(const readonly_cstring &str); readonly_cstring &assign(const char *str); readonly_cstring &assign(const char *chars, size_t n); readonly_cstring &assign(const char *begin, const char *end); bool empty() const; const char *c_str() const; operator const char*() const; char *reserve(size_t n); void swap(readonly_cstring &str) noexcept; }; int compare(const readonly_cstring &s1, const readonly_cstring &s2); int compare(const readonly_cstring &s1, const char *s2); int compare(const char *s1, const readonly_cstring &s2); bool operator==(const readonly_cstring &s1, const readonly_cstring &s2); bool operator!=(const readonly_cstring &s1, const readonly_cstring &s2); bool operator<(const readonly_cstring &s1, const readonly_cstring &s2); bool operator>(const readonly_cstring &s1, const readonly_cstring &s2); bool operator<=(const readonly_cstring &s1, const readonly_cstring &s2); bool operator>=(const readonly_cstring &s1, const readonly_cstring &s2); bool operator==(const readonly_cstring &s1, const char *s2); bool operator!=(const readonly_cstring &s1, const char *s2); bool operator<(const readonly_cstring &s1, const char *s2); bool operator>(const readonly_cstring &s1, const char *s2); bool operator<=(const readonly_cstring &s1, const char *s2); bool operator>=(const readonly_cstring &s1, const char *s2); bool operator==(const char *s1, const readonly_cstring &s2); bool operator!=(const char *s1, const readonly_cstring &s2); bool operator<(const char *s1, const readonly_cstring &s2); bool operator>(const char *s1, const readonly_cstring &s2); bool operator<=(const char *s1, const readonly_cstring &s2); bool operator>=(const char *s1, const readonly_cstring &s2); void swap(readonly_cstring &s1, readonly_cstring &s2) noexcept;
Простой класс неизменяемой строки с нулевым терминатором, автоматически управляющий памятью. Имеет простейшую предсказуемую структуру, что может быть полезно для обеспечения двоичной совместимости или в случаях, когда использование std::string нежелательно по каким-либо причинам. Функциональность класса также минимальна. Она обеспечивает копирование и хранение строки, а также доступ к ней на чтение. Модифицировать строку нельзя – только заменять полностью на другую.
Если Вам нужно хранить в классе строковое значение, то использование данного типа для строкового поля – это хороший вариант. Это более удобно, универсально и безопасно, чем массив символов (char[]) и часто более эффективно, чем std::string, хотя, конечно, менее универсально. Если строку предстоит часто редактировать (не считая полной замены), то лучше рассмотреть альтернативные варианты представления, например __vic::string_buffer. Класс readonly_cstring разработан не для этих целей.
readonly_cstring()
Создаёт пустую строку.
Постусловие: empty() == true
readonly_cstring(const char *str)
readonly_cstring(const readonly_cstring &str)
Создаёт копию str.
readonly_cstring(const char *chars, size_t n)
readonly_cstring(const char *begin, const char *end)
Создаёт строку из диапазона символов.
readonly_cstring &operator=(const char *str)
readonly_cstring &operator=(const readonly_cstring &str)
readonly_cstring &assign(const char *str)
Присваивает str.
readonly_cstring(readonly_cstring &&str) noexcept [C++11]
readonly_cstring &operator=(readonly_cstring &&str) noexcept [C++11]
Операции перемещения для режима C++11.
readonly_cstring &assign(const char *begin, const char *end)
readonly_cstring &assign(const char *chars, size_t n)
Присваивает строку из диапазона символов.
bool empty() const
Возвращает true, если строка пустая.
const char *c_str() const
operator const char*() const
Возвращает указатель на хранимую строку. Возвращаемый указатель никогда не бывает пустым.
char *reserve(size_t n)
Резервирует внутренний буфер в n символов и возвращает указатель на него. Бывает полезно для использования с функциями вроде std::sprintf().
Замечание: Используйте как можно реже данную опасную функцию!
void swap(readonly_cstring &str) noexcept
Меняется содержимым с str.
int compare(const readonly_cstring &s1, const readonly_cstring &s2)
int compare(const readonly_cstring &s1, const char *s2)
int compare(const char *s1, const readonly_cstring &s2)
Сравнивает две строки аналогично std::strcmp.
bool operator==(const readonly_cstring &s1, const readonly_cstring &s2)
...
bool operator>=(const char *s1, const readonly_cstring &s2)
Полный набор операторов сравнения readonly_cstring и const char * в различных сочетаниях.
void swap(readonly_cstring &s1, readonly_cstring &s2) noexcept
Специализация стандартного алгоритма.
class C { __vic::readonly_cstring st; public: explicit C(const char *str) : st(str) {} const char *get_str() const { return st; } };
class set_of_chars { public: constexpr set_of_chars(); template<class Iter> constexpr set_of_chars(Iter begin, Iter end); constexpr set_of_chars(std::initializer_list<char> set); // C++11 constexpr set_of_chars(const char *c_str); constexpr bool contains(char ch) const; constexpr void add(char ch); constexpr void remove(char ch); template<class Iter> constexpr void add(Iter begin, Iter end); constexpr void add(const char *c_str); constexpr void add(std::initializer_list<char> set); // C++11 template<class Iter> void assign(Iter begin, Iter end); void assign(const char *c_str) void assign(std::initializer_list<char> set); // C++11 void clear(); };
Компактная (всего 32 байта) и очень быстрая реализация множества символов. Операция contains() всегда выполняется за константное время, независимо от аргумента и количества элементов во множестве.
constexpr set_of_chars()
Создаёт пустое множество.
Постусловие: contains(char(ch)) == false для любого символа
template<class Iter> constexpr set_of_chars(Iter begin, Iter end)
constexpr set_of_chars(std::initializer_list<char> set) [C++11]
Создаёт множество, заполненное символами из указанного диапазона значений.
Замечание: constexpr только начиная с C++14!
constexpr set_of_chars(const char *c_str)
Создаёт множество, заполненное символами указанной C-строки, исключая нулевой терминатор.
Замечание: constexpr только начиная с C++14!
bool contains(char ch) const
Проверяет, содержит ли множество ch.
constexpr void add(char ch)
Добавляет ch во множество.
Постусловие: contains(ch) == trueЗамечание: constexpr только начиная с C++14!
constexpr void remove(char ch)
Удаляет ch из множества.
Постусловие: contains(ch) == falseЗамечание: constexpr только начиная с C++14!
template<class Iter> constexpr void add(Iter begin, Iter end)
constexpr void add(std::initializer_list<char> set) [C++11]
Вызывает add(ch) для каждого значения из диапазона.
Замечание: constexpr только начиная с C++14!
constexpr void add(const char *c_str)
Вызывает add(ch) для каждого символа C-строки, исключая нулевой терминатор.
Замечание: constexpr только начиная с C++14!
template<class Iter> void assign(Iter begin, Iter end)
void assign(const char *c_str)
void assign(std::initializer_list<char> set) [C++11]
Вызывает clear(), затем add() с указанными параметрами.
void clear()
Удаляет все элементы из множества.
Постусловие: contains(char(ch)) == false для любого символа
<stdint.h> из ISO C99 для C++98. Начиная с C++11 - просто перенаправление на <cstdint>.
Дополнительно содержит некоторые метафункции для шаблонного метапрограммирования.
Гарантируется, что следующие типы доступны в глобальном пространстве имён:
Гарантируется, что следующие типы доступны в глобальном пространстве имён:
Гарантируется, что следующие типы доступны в глобальном пространстве имён:
Гарантируется, что следующие типы доступны в глобальном пространстве имён:
Гарантируется, что следующие типы доступны в глобальном пространстве имён:
template<unsigned SizeInBytes> struct int_exactly_bytes { using type = <signed-integer-type-of-the-corresponding-size>; }; template<unsigned SizeInBytes> struct uint_exactly_bytes { using type = <unsigned-integer-type-of-the-corresponding-size>; }; // BEGIN C++11 template<unsigned N> using int_exact_bytes = typename int_exactly_bytes<N>::type; template<unsigned N> using uint_exact_bytes = typename uint_exactly_bytes<N>::type; // END C++11
Метафункции, возвращающие знаковый/беззнаковый целый тип для указанного размера в байтах. Более короткие псевдонимы доступны в режиме C++11 и выше. Допустимые значения для SizeInBytes: 1, 2, 4, 8.
typedef __vic::int_exactly_bytes< sizeof(void *) >::type my_intptr_t; // or in C++11 using my_intptr_t = __vic::int_exact_bytes< sizeof(void *) > assert( sizeof(my_intptr) == sizeof(intptr_t) );
C++ обёртки для std::FILE.
class stdio_file : private non_copyable { public: explicit stdio_file(std::FILE *fp = nullptr); stdio_file(const char *name, const char *mode); ~stdio_file(); // BEGIN C++11 stdio_file(stdio_file &&o) noexcept; stdio_file &operator=(stdio_file &&o) noexcept; // END C++11 bool open(const char *name, const char *mode); bool is_open() const; void close(); bool close_nt() noexcept; void swap(stdio_file &o) noexcept; std::FILE *detach_handle() noexcept; std::FILE *attach_handle(std::FILE *fp) noexcept; std::FILE *handle() const; operator std::FILE*() const; };
Тонкая RAII-обёртка для std::FILE *. Контролирует время жизни файла. Автоматический преобразователь типа позволяет использовать объекты данного типа в контекстах, требующих FILE *.
explicit stdio_file(std::FILE *fp = nullptr)
Создаёт обёртку для уже имеющегося указателя потока.
Предусловие: fp указывает на открытый файл или nullptr.
stdio_file(const char *name, const char *mode)
Вызывает open(name, mode). Необходимо проверить результат открытия последующим вызовом is_open()!
~stdio_file()
Вызывает std::fclose(), если is_open() == true.
stdio_file(stdio_file &&o) noexcept [C++11]
stdio_file &operator=(stdio_file &&o) noexcept [C++11]
Операции перемещения для режима C++11.
bool open(const char *name, const char *mode)
Вызывает std::fopen(name, mode). Возвращает true в случае успеха.
Предусловие: is_open() == false
bool is_open() const
Возвращает true если файл открыт.
void close()
Вызывает std::fclose(). Проверка на то, открыт ли файл, не производится! Если std::fclose() возвращает ошибку, то бросается исключение.
Предусловие: is_open() == trueПостусловие: is_open() == false
bool close_nt() noexcept
Аналог close(), но никогда не бросает исключений, а возвращает false в случае ошибок.
void swap(stdio_file &o) noexcept
Обменивается значением с o.
std::FILE *detach_handle() noexcept
Освобождает файл из-под контроля объекта.
Постусловие: is_open() == false
std::FILE *attach_handle(std::FILE *fp) noexcept
Помещает fp под контроль объекта и возвращает предыдущее хранимое значение.
Предусловие: fp – указатель на открытый файл или nullptr.Постусловие: handle() == fp
std::FILE *handle() const
Возвращает хранимое завёрнутое значение.
operator std::FILE*() const
Позволяет использовать объект как std::FILE *.
__vic::stdio_file file("file.txt", "w"); if(!file.is_open()) throw __vic::exception("Cannot open file"); std::fprintf(file, "Message"); file.close(); // fclose() также вызовется автоматически деструктором при исключениях
sread_result<char> read(std::FILE *fp);
Пытается прочитать байт из потока C. Возвращает успешный результат в случае успеха, неуспешный при достижении конца файла или бросает исключение в случае ошибки.
void write(std::FILE *fp, char ch);
Записывает байт в поток C. Бросает исключение в случае ошибки.
bool getline(std::FILE *fp, std::string &str, char delim = '\n');
Аналог std::getline для потоков C. Возвращает false, если конец файла достигнут раньше, чем что-то было прочитано.
#if __cpp_lib_string_view // since C++17 void decimal_to_number(std::string_view s, long long &res); void decimal_to_number(std::string_view s, long &res); void decimal_to_number(std::string_view s, int &res); void decimal_to_number(std::string_view s, short &res); void decimal_to_number(std::string_view s, signed char &res); void decimal_to_number(std::string_view s, unsigned long long &res); void decimal_to_number(std::string_view s, unsigned long &res); void decimal_to_number(std::string_view s, unsigned &res); void decimal_to_number(std::string_view s, unsigned short &res); void decimal_to_number(std::string_view s, unsigned char &res); template<class Integer> [[nodiscard]] Integer decimal_to_number(std::string_view s); #else // until C++17 void decimal_to_number(const std::string &s, long long &res); void decimal_to_number(const std::string &s, long &res); void decimal_to_number(const std::string &s, int &res); void decimal_to_number(const std::string &s, short &res); void decimal_to_number(const std::string &s, signed char &res); void decimal_to_number(const std::string &s, unsigned long long &res); void decimal_to_number(const std::string &s, unsigned long &res); void decimal_to_number(const std::string &s, unsigned &res); void decimal_to_number(const std::string &s, unsigned short &res); void decimal_to_number(const std::string &s, unsigned char &res); void decimal_to_number(const char *s, long long &res); void decimal_to_number(const char *s, long &res); void decimal_to_number(const char *s, int &res); void decimal_to_number(const char *s, short &res); void decimal_to_number(const char *s, signed char &res); void decimal_to_number(const char *s, unsigned long long &res); void decimal_to_number(const char *s, unsigned long &res); void decimal_to_number(const char *s, unsigned &res); void decimal_to_number(const char *s, unsigned short &res); void decimal_to_number(const char *s, unsigned char &res); template<class Integer> [[nodiscard]] Integer decimal_to_number(const std::string &s); template<class Integer> [[nodiscard]] Integer decimal_to_number(const char *s); #endif
Набор функций, преобразующих строки, содержащие десятичное представление целого числа, в один из стандартных целых типов C++. Входная строка может быть как C-строкой, так и std::string.
В отличие от стандартных преобразователей, вроде std::strtol(), производится строгая проверка на формат строки и диапазон значения. В частности, лидирующие пробелы и символы, не являющиеся цифрами, в конце не допустимы. Для беззнаковых типов недопустим символ '-', который std::strtoul() по непонятной причине воспринимает как корректный.
В случае ошибок бросаются исключения:
Для данных функций доступны две категории прототипов:
template<class T, class InputIterator> void decimal_to_number_range(InputIterator begin, InputIterator end, T &res); template<class T, class InputIterator> [[nodiscard]] T decimal_to_number_range(InputIterator begin, InputIterator end);
Функции являются полным аналогом функций decimal_to_number() за тем исключением, что на вход принимают диапазон символов вместо строки.
#if __cpp_lib_string_view // since C++17 template<class T> [[nodiscard]] number_parse_result<T> parse_decimal(std::string_view s); #else // until C++17 template<class T> [[nodiscard]] number_parse_result<T> parse_decimal(const std::string &s); template<class T> [[nodiscard]] number_parse_result<T> parse_decimal(const char *s); #endif template<class T, class InputIterator> [[nodiscard]] number_parse_result<T> parse_decimal(InputIterator begin, InputIterator end);
Аналог функций decimal_to_number(), но не бросает исключений. Возвращаемый объект может тестироваться в bool-контексте, что позволяет узнать об успехе операции. Также данный объект содержит число-результат в случае успеха, или код ошибки в противном случае. См. number_parse_result для более подробного описания.
if(auto res = __vic::parse_decimal<int>("123")) { int num = res.value(); // Use num }
template<class T> class decimal_parser { using status = number_parse_status; // только для краткости public: template<class InputIterator> [[nodiscard]] status parse(InputIterator begin, InputIterator end); #if __cpp_lib_string_view // since C++17 [[nodiscard]] status parse(std::string_view str); #else // until C++17 [[nodiscard]] status parse(const std::string &str); [[nodiscard]] status parse(const char *str); #endif [[nodiscard]] T result() const; };
Аналог функций decimal_to_number(), но ни бросает исключений. Вместо них parse() возвращает коды number_parse_status.
number_parse_status::ok
Удачно, результат может быть получен вызовом функции result().
number_parse_status::invalid_number
Строка не является корректным десятичным целым.
number_parse_status::unrepresentable
Строка, возможно, корректна, но результат не представим в диапазоне запрошенного типа (целочисленное переполнение).
template<class InputIterator> [[nodiscard]] status parse(InputIterator begin, InputIterator end)
[[nodiscard]] status parse(std::string_view str) [C++17]
[[nodiscard]] status parse(const std::string &str) [until C++17]
[[nodiscard]] status parse(const char *str) [until C++17]
Преобразует диапазон символов или строку в число.
Постусловие: Результат преобразования может быть получен вызовом result(), если возвращён number_parse_status::ok.
[[nodiscard]] T result() const
Возвращает результат преобразования последнего вызова parse().
Предусловие: Последний вызов parse() вернул number_parse_status::ok.
template<class T> bool to_number(const std::string &s, T &result) noexcept { __vic::decimal_parser<T> p; if(p.parse(s) != __vic::number_parse_status::ok) return false; result = p.result(); returt true; // 'result' содержит результат преобразования }
enum class number_parse_status : unsigned char { ok, invalid_number, unrepresentable }; using number_parse_status_t = number_parse_status; // for C++98
Коды статусов результата разбора.
typename number_parse_status_t
Используйте данное имя типа, если коду требуется совместимость с режимом C++98.
template<class T> class number_parse_result { public: typedef T value_type; number_parse_result(number_parse_status_t s); explicit number_parse_result(T n); number_parse_status_t status() const; explicit operator bool() const; bool has_value() const; T value() const; };
number_parse_status + результирующее значение в случае успеха.
number_parse_result(number_parse_status_t s)
Предусловие: s != number_parse_status::okПостусловие: status() == s
explicit number_parse_result(T n)
Постусловие: has_value() && value() == n
number_parse_status_t status() const
Статус операции разбора числа.
bool has_value() const
explicit operator bool() const
Возвращает status() == number_parse_status::ok.
T value() const
Число-результат.
Предусловие: has_value() == true
class string_buffer : public std::string { public: string_buffer(); explicit string_buffer(size_type n); string_buffer(const char *str); string_buffer(std::string str); string_buffer(string_ref sr); string_buffer(const std::string &str, size_type off, size_type n = npos); string_buffer(const char *char_buf, size_type n); string_buffer(const char *begin, const char *end); template<class InputIterator> string_buffer(InputIterator begin, InputIterator end); template<class T> string_buffer &operator<<(const T &v); string_buffer &operator=(string_ref sr); string_buffer &operator+=(string_ref sr); string_buffer &assign(string_ref sr); string_buffer &append(string_ref sr); // improved std::string calls string_buffer &assign(const std::string &str, size_type off, size_type n = npos); string_buffer &append(const std::string &str, size_type off, size_type n = npos); string_buffer &insert(size_type pos, const std::string &str, size_type off, size_type n = npos); string_buffer &reserve(size_type n); string_buffer &clear(); // missing container interface of std::string reference front(); reference back(); const_reference front() const; const_reference back() const; void pop_back(); operator const char *() const; }; string_buffer operator+(const string_buffer &s1, const string_buffer &s2); string_buffer operator+(const string_buffer &s1, const std::string &s2); string_buffer operator+(const std::string &s1, const string_buffer &s2); string_buffer operator+(const string_buffer &s1, const char *s2); string_buffer operator+(const char *s1, const string_buffer &s2); string_buffer operator+(const string_buffer &s, char ch); string_buffer operator+(char ch, const string_buffer &s); using msg = string_buffer;
Класс является улучшенным и расширенным std::string. Он имеет следующие преимущества:
str << "Error message: " << err_msg << "\n";
__vic::string_buffer st(4096); // Эффект аналогичен std::string st; st.reserve(4096);
for(int i=0; i<10; i++) str << "i = " << i << '\n';
std::string s1("Str"); const char *p = nullptr; s1.append(p); // Oops.... Null pointer access! __vic::string_buffer s2("Str"); s2.append(p); // Ok. s2 == "Str" still s2 = p; // Ok. s2.empty() == true
std::string fname(...); FILE *fp = fopen(fname.c_str(), "r"); __vic::string_buffer fname(...); FILE *fp = fopen(fname, "r");)
При всех этих улучшениях, объекты данного типа полностью совместимы по структуре с std::string и могут передаваться в контексты, требующие std::string. Никаких дополнительных членов-данных в классе нет.
Использование инсертера (оператора <<) данного класса – это простейший способ преобразовать число или указатель в строку. Механизм, конечно, не такой мощный, как использование std::ostringstream, например нельзя задать основание системы счисления или форматирование, но зато самый простой и эффективный. Для вывода диагностики, к примеру, обычно его вполне достаточно.
Также для данного типа введён синоним msg, который удобно использовать для конструирования составных сообщений на лету одним выражением без введения дополнительных переменных:
oresult res = db_open(db_name); if(res != 0) throw __vic::exception( __vic::msg(64) << "Cannot open DB " << db_name << ". res = " << res );
Как видно в примере, в конструктор в целях оптимизации передан максимальный ожидаемый размер строки.
string_buffer()
Создаёт пустую строку.
Постусловие: empty() == true
explicit string_buffer(size_type n)
Вызывает reserve(n).
Постусловие: capacity() >= n
string_buffer(const char *str)
string_buffer(std::string str)
Создаёт копию str.
string_buffer(const std::string &str, size_type off, size_type n = npos)
Создаёт копию подстроки str.
string_buffer(const char *char_buf, size_type n)
Создаёт строку из буфера и его длины.
string_buffer(string_ref sr)
string_buffer(const char *begin, const char *end)
template<class InputIterator> string_buffer(InputIterator begin, InputIterator end)
Создаёт строку из диапазона символов.
template<class T> string_buffer &operator<<(const T &v)
Вызывает to_text_append(v, *this).
string_buffer &operator=(string_ref sr)
string_buffer &operator+=(string_ref sr)
string_buffer &assign(string_ref sr)
string_buffer &append(string_ref sr)
Операции для string_ref.
string_buffer &reserve(size_type n)
string_buffer &clear()
Вызывают одноимённую операцию std::string и дополнительно возвращают ссылку на себя, что позволяет использовать вызовы в составных выражениях.
reference front()
reference back()
const_reference front() const
const_reference back() const
void pop_back()
Недостающие операции интерфейса std::string в C++98.
operator const char *() const
Вызывает std::string::c_str().
string_buffer operator+(const string_buffer &s1, const string_buffer &s2)
string_buffer operator+(const string_buffer &s1, const std::string &s2)
string_buffer operator+(const std::string &s1, const string_buffer &s2)
string_buffer operator+(const string_buffer &s1, const char *s2)
string_buffer operator+(const char *s1, const string_buffer &s2)
string_buffer operator+(const string_buffer &s, char ch)
string_buffer operator+(char ch, const string_buffer &s)
Конкатенация строк и символов.
template<class charT> class basic_string_ref { public: using value_type = charT; using iterator = const value_type *; using const_iterator = iterator; // Constructors basic_string_ref(); basic_string_ref(const charT *str); basic_string_ref(const charT *chars, size_t n); basic_string_ref(const charT *begin, const charT *end); template<class Traits, class Alloc> explicit basic_string_ref( const std::basic_string<charT,Traits,Alloc> &str); basic_string_ref( typename std::basic_string<charT>::const_iterator begin, typename std::basic_string<charT>::const_iterator end); // BEGIN C++11 basic_string_ref(std::initializer_list<charT> ); // END C++11 #if __cpp_lib_string_view // since C++17 template<class Traits> basic_string_ref(std::basic_string_view<charT,Traits> s); operator std::basic_string_view<charT>() const; #endif // Accessors iterator begin() const; iterator end() const; iterator cbegin() const; iterator cend() const; charT front() const; charT back() const; charT operator[](size_t i) const; const charT *data() const; bool empty() const; size_t size() const; size_t length() const; int compare(basic_string_ref s) const; // Converters std::basic_string<charT> str() const; template<class Traits> std::basic_string<charT,Traits> str() const; template<class Traits, class Alloc> std::basic_string<charT,Traits,Alloc> str(const Alloc &a = Alloc())const; operator std::basic_string<charT>() const; }; using string_ref = basic_string_ref<char> ; template<class charT> bool operator==(basic_string_ref<charT> s1, basic_string_ref<charT> s2); template<class charT> bool operator!=(basic_string_ref<charT> s1, basic_string_ref<charT> s2); template<class charT> bool operator<(basic_string_ref<charT> s1, basic_string_ref<charT> s2); template<class charT> bool operator>(basic_string_ref<charT> s1, basic_string_ref<charT> s2); template<class charT> bool operator<=(basic_string_ref<charT> s1, basic_string_ref<charT> s2); template<class charT> bool operator>=(basic_string_ref<charT> s1, basic_string_ref<charT> s2); #ifdef __VIC_DEFINE_OSTREAM_INSERTERS template<class charT, class Traits> std::basic_ostream<charT,Traits> &operator<<( std::basic_ostream<charT,Traits> &os, const basic_string_ref<charT> &sr); #endif
Класс преставляет собой ссылку на протяжённый диапазон символов, доступных только для чтения. При использовании в качестве возвращаемого типа он значительно легче, чем std::string, потому что не требует копирования строки и выделения дополнительной памяти. Но при использовании в контексте, требующем std::string, происходит автоматическое преобразование. Рассмотрим пример:
class C { std::string v; public: std::string get_v_1() const { return v; } __vic::string_ref get_v_2() const { return v; } };
Как видно, в классе хранится поле в виде строки. Для доступа на чтение к нему описаны две функции. Первая традиционно возвращает std::string, вторая – string_ref. При доступе через первую функцию каждый раз создаётся временная строка, при доступе через первую – просто возвращается ссылка.
Другой сценарий использования – это входной аргумент, строка используемая только для чтения. Класс - универсальная замена для const std::string &. В большинстве случаев он также может быть использован вместо const char *. Накладными расходами в этом случае будет проход по строке в поисках NULL-терминатора, который всё равно нужно сделать в случаях, когда в любом случае нужен конец строки или её длина. Рассмотрим три набора перегруженных функций:
void f1(const std::string & ); void f2(const std::string & ); void f2(const char * ); void f3(string_ref );
Каждый из них может быть использован как
fx("Nul-terminated string");
так и как
fx(std::string("std::string"));
Но в случае с f1() в первом случае будет лишнее копирование строки в кучу только для того чтобы его прочитать. В случае с f2() нужно писать несколько перегрузок функции, и если с одним аргументом это не является сильно утруждающим, то при увеличении их количества количество комбинаций сильно увеличивается. Последний вариант с f3() является таким же удобным и универсальным, как с f1(), но при этом он более «дружественный» к строковым литералам и строкам из мира C – копирования их в динамическую память и превращения в std::string не происходит.
basic_string_ref()
Постусловие: empty() == true
basic_string_ref(const charT *str)
template<class Traits, class Alloc> basic_string_ref(const std::basic_string<charT,Traits,Alloc> &str)
Создают ссылку на str.
Постусловие: *this == str
basic_string_ref(const charT *chars, size_t n)
basic_string_ref(const charT *begin, const charT *end)
basic_string_ref(const charT *chars, size_t n)
basic_string_ref( typename std::basic_string<charT>::const_iterator begin, typename std::basic_string<charT>::const_iterator end)
basic_string_ref(std::initializer_list<charT> ) [C++11]
Создают ссылку на диапазон символов.
template<class Traits>
basic_string_ref(std::basic_string_view<charT,Traits> s) [C++17]
operator std::basic_string_view<charT>() const [C++17]
Преобразователи из/в std::basic_string_view.
iterator begin() const
iterator cbegin() const
const charT *data() const
Начало диапазона символов.
iterator end() const
iterator cend() const
Конец диапазона символов.
charT front() const
charT back() const
Первый и последний символ диапазона, соответственно.
Предусловие: !empty()
charT operator[](size_t i) const
i-й символ строки.
Предусловие: i < length()
bool empty() const
Возвращает begin() == end().
size_t size() const
size_t length() const
Длина строки.
int compare(basic_string_ref s) const
Сравнивает строку с s. Возвращаемые значения аналогичны std::string::compare().
std::basic_string<charT> str() const
template<class Traits> std::basic_string<charT,Traits> str() const
template<class Traits, class Alloc> std::basic_string<charT,Traits,Alloc> str(const Alloc &a = Alloc()) const
Явный преобразователь в std::basic_string.
operator std::basic_string<charT>() const
Неявный преобразователь в std::basic_string.
template<class charT> bool operator==(basic_string_ref<charT> s1, basic_string_ref<charT> s2)
template<class charT> bool operator!=(basic_string_ref<charT> s1, basic_string_ref<charT> s2)
template<class charT> bool operator<(basic_string_ref<charT> s1, basic_string_ref<charT> s2)
template<class charT> bool operator>(basic_string_ref<charT> s1, basic_string_ref<charT> s2)
template<class charT> bool operator<=(basic_string_ref<charT> s1, basic_string_ref<charT> s2)
template<class charT> bool operator>=(basic_string_ref<charT> s1, basic_string_ref<charT> s2)
Полный набор операторов сравнения.
template<class charT, class Traits> std::basic_ostream<charT,Traits> &operator<<( std::basic_ostream<charT,Traits> &os, const basic_string_ref<charT> &sr)
Инсертер в стандартный выходной поток. Определён (а также <ostream> включён) только, если определён макрос __VIC_DEFINE_OSTREAM_INSERTERS перед включением.
C c; // описание класса см. выше __vic::string_ref s = c. get_v_2(); // печать строки разными способами for(__vic::string_ref::iterator it = s.begin(); it != s.end(); ++it) std::cout << *it; // C++11 for(auto ch : s) std::cout << ch; std::copy(s.begin(), s.end(), std::ostream_iterator<char>(std::cout)); std::cout << s; // автоматическое преобразование в std::string std::string ss = s;
Различные утилиты для работы со строками.
char *trim(char *str); char *trim_front(char *str); char *trim_back(char *str); char *trim(char *str, char ch); char *trim_front(char *str, char ch); char *trim_back(char *str, char ch); char *trim(char *str, const char *set); char *trim_front(char *str, const char *set); char *trim_back(char *str, const char *set); std::string &trim(std::string &str); std::string &trim_front(std::string &str); std::string &trim_back(std::string &str); std::string &trim(std::string &str, char ch); std::string &trim_front(std::string &str, char ch); std::string &trim_back(std::string &str, char ch); std::string &trim(std::string &str, const char *set); std::string &trim_front(std::string &str, const char *set); std::string &trim_back(std::string &str, const char *set); std::string trimmed(const std::string &str); std::string trimmed_front(const std::string &str); std::string trimmed_back(const std::string &str); std::string trimmed(const std::string &str, char ch); std::string trimmed_front(const std::string &str, char ch); std::string trimmed_back(const std::string &str, char ch); std::string trimmed(const std::string &str, const char *set); std::string trimmed_front(const std::string &str, const char *set); std::string trimmed_back(const std::string &str, const char *set);
Набор функций, обрезающих нежелательные символы по краям строки. Обрезаемые символы можно задавать. Задать можно как одиночный символ ch, так и набор set. Если набор не задан, то подразумеваются все пробельные ASCII-символы. Используются следующие правила именования:
Функции trim модифицируют переданную строку и возвращают указатель или ссылку на неё. Если это по каким-то причинам нежелательно, то следует использовать набор функций trimmed.
Реализация функций trim выполнена с расчётом, что у строки может не быть символов, подлежащих обрезанию. В подобных случаях никаких модификаций не производится, и функция возвращает управление сразу после проверки. С точки зрения эффективности, затраты на такие вызовы минимальны.
Все значения nullptr воспринимаются как пустая строка.
char st1[] = "\t value \n"; // CHOICE: __vic::trim(st1); // result: "value" __vic::trim_front(st1); // result: "value \n" __vic::trim_back(st1); // result: "\t value" std::string st2("...value123"); // CHOICE: // trim dot chars __vic::trim_front(st1, '.'); // result: "value123" // trim all digits __vic::trim_back(st1, "123456789"); // result: "...value"
char *sift(char *str, const char *trash_chars); std::string &sift(std::string &str, const char *trash_chars);
Удаляет из строки все символы из указанного набора. Все значения nullptr воспринимаются как пустая строка.
char st[] = "..ab.c..d.e."; __vic::sift(st, "."); assert(std::strcmp(st, "abcde") == 0);
template<class Pred> char *sift(char *str, Pred pred); template<class Pred> std::string &sift(std::string &str, Pred pred);
Удаляет из строки все символы, удовлетворяющие предикату pred. Все значения nullptr воспринимаются как пустая строка.
std::string &pad_front(std::string &str, size_t size, char pad_ch = ' '); char *pad_front(char *str, size_t size, char pad_ch = ' ');
Дополняет строку до длины size в начале символами pad_ch. Ничего не происходит, если строка уже имеет длину size или большую, либо указатель - null. Возвращает str.
std::string &pad_back(std::string &str, size_t size, char pad_ch = ' '); char *pad_back(char *str, size_t size, char pad_ch = ' ');
Дополняет строку до длины size в конце символами pad_ch. Ничего не происходит, если строка уже имеет длину size или большую, либо указатель - null. Возвращает str.
bool starts_with(const char *s, char pref); bool starts_with(const char *s, const char *pref); bool starts_with(const char *s, const char *pref, size_t pref_len); #if __cpp_lib_string_view // C++17 bool starts_with(std::string_view s, char pref); bool starts_with(std::string_view s, std::string_view pref); bool starts_with(std::string_view s, const char *pref); #else // until C++17 bool starts_with(const std::string &s, char pref); bool starts_with(const std::string &s, const char *pref); bool starts_with(const std::string &s, const std::string &pref); bool starts_with(const std::string &s, const char *pref, size_t pref_len); #endif bool starts_with(const char *s, size_t s_len, char pref); bool starts_with(const char *s, size_t s_len, const char *pref); bool starts_with(const char *s, size_t s_len, const char *pref, size_t pref_len);
Возвращает true, если строка s начинается с указанного префикса.
#if __cpp_lib_string_view // C++17 bool ends_with(std::string_view s, char suff); bool ends_with(std::string_view s, std::string_view suff); #else // until C++17 bool ends_with(const char *s, char suff); bool ends_with(const char *s, const char *suff); bool ends_with(const char *s, const char *suff, size_t suff_len); bool ends_with(const char *s, size_t s_len, const char *suff); bool ends_with(const std::string &s, char suff); bool ends_with(const std::string &s, const char *suff); bool ends_with(const std::string &s, const std::string &suff); bool ends_with(const std::string &s, const char *suff, size_t suff_len); #endif bool ends_with(const char *s, size_t s_len, char suff); bool ends_with(const char *s, size_t s_len, const char *suff, size_t suff_len);
Возвращает true, если строка s оканчивается указанным суффиксом.
Обобщенные (generic) функции для манипуляции С-строками независимо от используемого ими типа символов, подобно std::char_traits<>. Использование данных функций в шаблонах нередко значительно сокращает потребность в написании специализаций для различных типов символов.
Все функции находятся в пространстве имён __vic::tchar.
Большинство функций являются просто обобщёнными обёртками для функций, вроде strcpy, wcscpy и т.п. Поисковые функции имеют более осмысленные имена, чем их аналоги в билиотеке C, и унифицированные параметры: они всегда принимают указатели и никогда индексы. Также набор функций дополнен «логически симметричными», но отсутствующими в стандартной билиотеке. Поисковые функции возвращают nullptr в случае неудачи.
template<class charT> charT *generic_dup(const charT *st) { namespace tchar = __vic::tchar; charT *st_copy = new charT[tchar::length(st) + 1]; tchar::copy(st_copy, st); return st_copy; }
template<class charT> size_t tchar::length(const charT *str);
Длина строки в элементах. Обобщённый strlen / wcslen.
template<class charT> bool tchar::empty(const charT *str);
Проверяет, является ли str nullptr или пустой строкой.
namespace tchar { template<class charT> const charT *end(const charT *str); template<class charT> charT *end(charT *str); }
Указатель на NULL-терминатор. Обобщённый strchr(str, '\0') / wcschr(str, L'\0').
template<class charT> int tchar::compare(const charT *str1, const charT *str2);
Сравнивает две строки. Обобщённый strcmp / wcscmp.
template<class charT> bool tchar::equal(const charT *str1, const charT *str2);
Проверяет двестроки на равенство.
template<class charT> charT *tchar::copy(charT *dest, const charT *src);
Копирует строку. Обобщённый strcpy / wcscpy.
template<class charT> charT *tchar::move(charT *dest, const charT *src);
Сдвигает строку в памяти (memmove).
template<class charT> charT *tchar::concat(charT *dest, const charT *src);
Конкатенирует две строки. Обобщённый strcat / wcscat.
namespace tchar { template<class charT> const charT *find(const charT *str, charT ch); template<class charT> charT *find(charT *str, charT ch); template<class charT> const charT *find(const charT *str, const charT *sub); template<class charT> charT *find(charT *str, const charT *sub); }
Ищет первое вхождение символа или подстроки. Обобщённые strchr / wcschr / strstr / wcsstr.
namespace tchar { template<class charT> const charT *rfind(const charT *str, charT ch); template<class charT> charT *rfind(charT *str, charT ch); }
Ищет последнее вхождение символа. Обобщённые strrchr / wcsrchr.
namespace tchar { template<class charT, class Pred> const charT *find_if(const charT *str, Pred pred); template<class charT, class Pred> charT *find_if(charT *str, Pred pred); }
Ищет первое вхождение символа, удовлетворяющего указанному предикату.
namespace tchar { template<class charT, class Pred> const charT *find_if_not(const charT *str, Pred pred); template<class charT, class Pred> charT *find_if_not(charT *str, Pred pred); }
Ищет первое вхождение символа, не удовлетворяющего указанному предикату.
namespace tchar { template<class charT, class Pred> const charT *rfind_if(const charT *str, Pred pred); template<class charT, class Pred> charT *rfind_if(charT *str, Pred pred); }
Ищет последнее вхождение символа, удовлетворяющего указанному предикату.
namespace tchar { template<class charT, class Pred> const charT *rfind_if_not(const charT *str, Pred pred); template<class charT, class Pred> charT *rfind_if_not(charT *str, Pred pred); }
Ищет последнее вхождение символа, не удовлетворяющего указанному предикату.
namespace tchar { template<class charT> const charT *find_first_of(const charT *str, const charT *set); template<class charT> charT *find_first_of(charT *str, const charT *set); }
Ищет первое вхождение символа из указанного набора. Обобщённый strpbrk / wcspbrk.
namespace tchar { template<class charT> const charT *find_first_not_of(const charT *str, const charT *set); template<class charT> charT *find_first_not_of(charT *str, const charT *set); }
Ищет первое вхождение символа, отсутствующего в указанном наборе. Обобщённый strspn / wcsspn.
namespace tchar { template<class charT> const charT *find_last_of(const charT *str, const charT *set); template<class charT> charT *find_last_of(charT *str, const charT *set); }
Ищет последнее вхождение символа из указанного набора.
namespace tchar { template<class charT> const charT *find_last_not_of(const charT *str, const charT *set); template<class charT> charT *find_last_not_of(charT *str, const charT *set); }
Ищет последнее вхождение символа, отсутсвующего в указанном наборе.
namespace tchar { template<class charT> const charT *skip(const charT *str, charT ch); template<class charT> charT *skip(charT *str, charT ch); }
Пропускает все вхождения указанного символа и возвращает указатель. Если другие символы в строке отсутствуют, возвращает указатель на NULL-терминатор.
Поддержка вычислительных потоков.
class thread : private non_copyable
{
public:
class id;
using native_handle_type = <implementation-defined>;
thread();
virtual ~thread();
// BEGIN C++11
thread(thread &&o) noexcept;
thread &operator=(thread &&o) noexcept;
// END C++11
void start();
void cancel();
void join();
bool alive() const;
bool joinable() const;
void kill(int signo); // POSIX only
id get_id() const;
native_handle_type handle() const;
protected:
virtual void worker() = 0;
};
Абстрактный базовый класс потоков. Реализует pattern «Active object». Унаследуйте данный класс и определите функцию worker(), содержимое которой будет выполнено в новом потоке после вызова start(). Затем где-то в Вашей программе Вы должны будете вызвать join() для освобождения ресурсов ОС, ассоциированных с порождённым потоком.
thread()
Постусловие: joinable() == false
~thread()
Вызывает std::terminate(), если нарушено предусловие.
Предусловие: joinable() == false || alive() == false
thread(thread &&o) noexcept [C++11]
Перемещающий конструктор для режима C++11.
thread &operator=(thread &&o) noexcept [C++11]
Перемещающее присваивание для режима C++11. Вызывает std::terminate(), если нарушено предусловие.
Предусловие: joinable() == false || alive() == false
void start()
Порождает новый поток и вызывает в нём worker().
Предусловие: joinable() == falseПостусловие: joinable() == true
void cancel()
Прерывает выполнение потока.
Предусловие: joinable() == trueПостусловие: joinable() == true
void join()
Ждёт завершения работы потока, если он выполняется в данный момент, и делает его joinable() == false.
Предусловие: joinable() == trueПостусловие: joinable() == false
bool alive() const
Возращает true, если выполнение потока ещё не завершилось (он находится в функции worker()).
Предусловие: joinable() == true
bool joinable() const
Возвращает true, если объект имеет соответсвующий объект ОС (поток), созданный вызовом start() и ещё не уничтоженный вызовом join().
void kill(int signo) [POSIX]
Посылает сигнал потоку сигнал signo.
Предусловие: joinable() == true
id get_id() const
Возвращает ID потока.
native_handle_type handle() const
Возвращает дескриптор потока, используемый в данной ОС.
class thread::id { public: id(); explicit operator bool() const; native_handle_type handle() const; }; bool operator==(thread::id a, thread::id b); bool operator!=(thread::id a, thread::id b);
Уникальный идентификатор потока. Может содержать значение, ассоциированное с потоком или специальное значение, не ассоциированное ни с одним потоком.
id()
Создаёт специальное значение не ассоциированное ни с одним потоком.
Постусловие: bool(*this) == false
explicit operator bool() const
Возвращает true, если объект хранит ID какого-то потока.
native_handle_type handle() const
Возвращает дескриптор потока, используемый в данной ОС.
Предусловие: bool(*this) == true
bool operator==(thread::id a, thread::id b)
bool operator!=(thread::id a, thread::id b)
Проверяет, ассоциированы ли a и b с одним и тем же потоком (либо оба содержат значение по умолчанию).
Инвариант: id() == id()
namespace this_thread { thread::id get_id(); void sleep_ms(unsigned msec); }
Набор функций для манипуляции с текущим (вызывающим) потоком.
thread::id get_id()
Возвращает ID вызывающего потока.
void sleep_ms(unsigned msec)
Приостанавливает выполнение вызывающего потока на указанное время в миллисекундах.
[[noreturn]] void throw_errno(const char *prompt); [[noreturn]] void throw_errno(const char *prompt, int err_no);
Бросает исключение, содержащее глобальное значение errno или данное err_no, соответственно. По умолчанию в данный момент типом исключения будет libc_error, однако это можно изменить во время компоновки переопределением данных функций. Например, можно использовать std::system_error. Для этого просто создайте cpp-файл со следующим содержимым в своём проекте:
#include<__vic/throw_errno.h> #include<system_error> //---------------------------------------------------------------------------- // Override library functions to throw std::system_error //---------------------------------------------------------------------------- void __vic::throw_errno(const char *prompt, int err_no) { throw std::system_error(err_no, std::system_category(), prompt); } //----------------------------------------------------------------------------
Переопределения одной функции достаточно, так как вторая просто вызывает throw_errno(prompt, errno).
ssize_t written = ::write(fd, buf, buf_size); if(written < 0) __vic::throw_errno("write"); // ...
Generic преобразователи в текстовое представление.
void to_text_append(long long n, std::string &str); void to_text_append(long n, std::string &str); void to_text_append(int n, std::string &str); void to_text_append(short n, std::string &str); void to_text_append(signed char n, std::string &str); void to_text_append(unsigned long long n, std::string &str); void to_text_append(unsigned long n, std::string &str); void to_text_append(unsigned n, std::string &str); void to_text_append(unsigned short n, std::string &str); void to_text_append(unsigned char n, std::string &str); void to_text_append(long double n, std::string &str); void to_text_append(double n, std::string &str); void to_text_append(float n, std::string &str); void to_text_append(bool f, std::string &str); void to_text_append(const void *p, std::string &str); void to_text_append(const std::string &st, std::string &s); void to_text_append(const char *st, std::string &s); void to_text_append(char ch, std::string &s); #if __cpp_lib_string_view // C++17 void to_text_append(std::string_view sv, std::string &s); #endif
Преобразует первый аргумент в некоторое текстовое представление и добавляет его ко второму аргументу.
Данная функция являет собой customization point (подобно std::swap) для библиотеки. Пользователи могут определять перегрузки для своих типов (в том же самом namespace, где определён тип), чтобы "научить" библиотеку как создавать текст из значения указанного типа.
void to_text_append(long long n, std::string &str)
void to_text_append(long n, std::string &str)
void to_text_append(int n, std::string &str)
void to_text_append(short n, std::string &str)
void to_text_append(signed char n, std::string &str)
void to_text_append(unsigned long long n, std::string &str)
void to_text_append(unsigned long n, std::string &str)
void to_text_append(unsigned n, std::string &str)
void to_text_append(unsigned short n, std::string &str)
void to_text_append(unsigned char n, std::string &str)
void to_text_append(long double n, std::string &str)
void to_text_append(double n, std::string &str)
void to_text_append(float n, std::string &str)
Преобразуют число в десятичное представление.
void to_text_append(bool f, std::string &str)
Преобразует булев тип в 0 или 1.
void to_text_append(const void *p, std::string &str)
Преобразует указатель в некоторое платформо-специфичное представление.
void to_text_append(const std::string &st, std::string &s)
void to_text_append(const char *st, std::string &s)
void to_text_append(char ch, std::string &s)
void to_text_append(std::string_view sv, std::string &s) [C++17]
Добавляет указанную строку или символ к выходной строке.
Замечание: Значение nullptr в перегрузке, принимающей const char *, считается пустой строкой.
int n = 5; std::string st = "n = "; __vic::to_text_append(n, st); assert(st == "n = 5");
Поддержка метапрограммирования с помощью шаблонов.
Все метафункции-предикаты имеют член в виде булевой статической константы по имени value и, как правило, порождены от integral_constant.
Все метафункции-преобразователи типов имеют член-тип по имени type, являющийся результатом преобразования.
Все шаблонные псевдонимы доступны только в режиме C++11.
template<class T, T Val> struct integral_constant { using value_type = T; using type = integral_constant<T, Val>; static constexpr T value = Val; };
Базовый класс большиства метафункций с value.
using true_type = integral_constant<bool, true>;
Базовый класс метафункций-предикатов со значением true.
using false_type = integral_constant<bool, false>;
Базовый класс метафункций-предикатов со значением false.
template<class T1, class T2> struct is_same;
Предикат. Истинен, если T1 и T2 - это в точности один и тот же тип.
template<class T> struct is_const;
Предикат. Истинен, если T имеет квалификатор const.
template<class T> struct is_byte;
Предикат. Истинен, если T - один из типов, представляющих байт:
template<class To, class From> constexpr To byte_cast(From v);
Приводит значение типа From к типу To, если оба типа представляют байт (см. is_byte).
template<class T> struct is_signed_integer;
Предикат. Истинен, если T - «стандартный целый тип со знаком» (см. Стандарт).
template<class T> struct is_unsigned_integer;
Предикат. Истинен, если T - «стандартный целый беззнаковый тип» (см. Стандарт).
template<class... B> struct conjunction;
Предикат. Конъюнкция предикатов B.... Ложен тогда и только тогда, когда ложен один из B....
template<class... B> struct disjunction;
Предикат. Дизъюнкция предикатов B.... Истинен тогда и только тогда, когда истинен один из B....
template<class B> struct negation;
Предикат. Логическое отрицание предиката B.
template<class T> struct remove_const; template<class T> using remove_const_t = typename remove_const<T>::type;
Преобразователь типа. Удаляет у типа самый верхний квалификатор const, либо просто возвращает T, если такого квалификатора нет.
template<class T> struct remove_volatile; template<class T> using remove_volatile_t = typename remove_volatile<T>::type;
Преобразователь типа. Удаляет у типа самый верхний квалификатор volatile, либо просто возвращает T, если такого квалификатора нет.
template<class T> struct remove_cv; template<class T> using remove_cv_t = typename remove_cv<T>::type;
Преобразователь типа. Удаляет у типа самые верхние квалификаторы const и/или volatile, либо просто возвращает T, если таких квалификаторов нет.
template<class T> struct remove_reference; template<class T> using remove_reference_t = typename remove_reference<T>::type;
Преобразователь типа. Возвращает тип, на который ссылается T, либо просто возвращает T, если он не является ссылкой.
template<class T> struct remove_cvref; template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
Преобразователь типа. Применяет remove_reference, затем remove_cv к T.
template<class T> struct remove_pointer; template<class T> using remove_pointer_t = typename remove_pointer<T>::type;
Преобразователь типа. Возвращает тип, на который указывает T, либо просто возвращает T, если он не является указателем.
template<bool Cond, class Then, class Else> struct conditional; template<bool Cond, class Then, class Else> using conditional_t = typename conditional<Cond, Then, Else>;
Преобразователь типа. Возвращает Then, если Cond == true. В противном случае - Else.
template<bool Test, class T = void> struct enable_if { using type = T; }; template<class T> struct enable_if<false, T> {}; template<bool Test, class T = void> struct disable_if : enable_if<!Test, T> {};
Классические инструменты для фокусов со SFINAE.
template<size_t... I> struct index_sequence { static constexpr size_t size() { return sizeof...(I); } }; template<size_t Size> using make_index_sequence = index_sequence<0, 1, ..., Size-1>;
Реализация std::index_sequence из C++14 для C++11.
Утилиты для поддержки Unicode.
using unicode_t = char32_t; // since C++11 // или using unicode_t = uint_least32_t; // C++98
Тип, предназначениый для хранения Unicode code point.
template<class UTFReader, class UTFWriter> void utf_transcode(UTFReader r, UTFWriter w);
Алгоритм, читающий code points типа unicode_t из UTFReader, используя r(), и записывающий их в UTFWriter, используя w().
constexpr unicode_t unicode_max = 0x10FFFF; constexpr unicode_t unicode_bom = 0xFEFF; constexpr unicode_t unicode_replacement_char = 0xFFFD;
Именованные константы некоторых полезных Unicode code points.
enum class utf8::status : unsigned char { ok = 0, eof, // Errors no_leading_byte, truncated_code_point, overlong_encoding, code_point_too_big }; using utf8::status_t = utf8::status; // for C++98
Значения, возвращаемые функцией parse() класса utf8::reader.
constexpr bool utf8::is_error(utf8::status s);
Возвращает false для значений utf8::status::ok и utf8::status::eof. В остальных случаях возвращается true.
namespace utf8 { class bad_encoding; // public std::exception class no_leading_byte; class truncated_code_point; class overlong_encoding; class code_point_too_big; } // namespace
Классы исключений, бросаемые функцией read() класса utf8::reader. Все исключения порождены от абстактного базового класса utf8::bad_encoding. Эквивалентные коды статусов описаны в utf8::status.
class utf8::read_result { public: read_result(utf8::status_t s); read_result(unicode_t v); unicode_t value() const; utf8::status_t status() const; explicit operator bool() const; };
Результат чтения, возвращаемый функцией utf8::reader::parse().
read_result(utf8::status_t s)
Предусловие: s != utf8::status::okПостусловие: status() == s
unicode_t value() const
Возвращает прочитанное значение.
Предусловие: status() == utf8::status::ok
utf8::status_t status() const
Возвращает статус операции чтения.
explicit operator bool() const
То же самое, что status() == utf8::status::ok.
sread_result<unicode_t> utf8::convert_or_throw(utf8::read_result r);
Преобразует utf8::read_result в sread_result. Бросает исключение из __vic/utf8/exceptions.h, если is_error(r.status()).
template<class ByteSReader> class utf8::reader { public: using byte_reader_type = ByteSReader; ByteSReader &get_byte_reader(); const ByteSReader &get_byte_reader() const; template<class... Args> explicit reader(Args&&... args); // since C++11 reader(); // C++98 only explicit reader(ByteSReader r); // C++98 only utf8::read_result parse(); sread_result<unicode_t> read() sread_result<unicode_t> operator()(); }; template<class ByteSReader> utf8::reader<ByteSReader> utf8::make_reader(ByteSReader r);
Вычитывает UTF-8 code points из последовательности байтов. Последовательность читается посредством ByteSReader, который моделирует sreader<unsigned char> (см. S-readers).
ByteSReader &get_byte_reader()
const ByteSReader &get_byte_reader() const
Возвращает ссылку на используемый byte reader.
template<class... Args>
explicit reader(Args&&... args) [C++11]
Передаёт все параметры в используемый byte reader.
reader() [C++98 only]
explicit reader(ByteSReader r) [C++98 only]
Конструкторы для режима C++98.
utf8::read_result parse()
Пытается извлечь следующий code point из последовательности байтов, используя ByteSReader. В случае успеха возвращается прочитанный code point. Если байты кончились, возвращается utf8::status::eof. Другие значения возвращаются в случае ошибок, подробности см. в utf8::status. Для доступа к отдельным байтам используется ByteSReader::operator().
Замечание: Функция сама по себе не бросает исключений, но их может бросать ByteSReader::operator().
sread_result<unicode_t> read()
sread_result<unicode_t> operator()()
То же самое, что parse(), но возвращает sread_result в случае успеха или при достижении конца файла. Бросает исключения из __vic/utf8/exceptions.h в остальных случаях.
template<class ByteSReader> utf8::reader<ByteSReader> utf8::make_reader(ByteSReader r)
Создаёт UTF-8 reader используя указанный ByteSReader.
#include<__vic/utf8/reader.h> #include<__vic/sreaders/string.h> #include<string> #include<cstdint> #include<iostream> // C++11 using utf8_string_reader = __vic::utf8::reader<__vic::string_sreader>; // C++98 struct utf8_string_reader : __vic::utf8::reader<__vic::string_sreader> { explicit utf8_string_reader(const std::string &s) : __vic::utf8::reader<__vic::string_sreader>(__vic::string_sreader(s)) {} }; void print_utf8_code_points(const string &s) { utf8_string_reader r(s); while(__VIC_SREAD_RESULT(unicode_t) cp = r()) std::cout << uint_least32_t(cp.value()) << '\n'; }
template<class ByteSWriter> class utf8::writer { public: using byte_writer_type = ByteSWriter; ByteSWriter &get_byte_writer(); const ByteSWriter &get_byte_writer() const; template<class... Args> explicit writer(Args&&... args); // since C++11 writer(); // C++98 only explicit writer(ByteSWriter w); // C++98 only void write(unicode_t cp); void operator()(unicode_t cp); }; template<class ByteSWriter> utf8::writer<ByteSWriter> utf8::make_writer(ByteSWriter w);
Пишет UTF-8 code points в последовательность байтов. Для вывода байтов используется ByteSWriter, моделирующий swriter<unsigned char> (см. S-writers).
ByteSWriter &get_byte_writer()
const ByteSWriter &get_byte_writer() const
Возвращает ссылку на используемый byte writer.
template<class... Args>
explicit writer(Args&&... args) [C++11]
Передаёт все параметры в используемый byte writer.
writer() [C++98 only]
explicit writer(ByteSWriter r) [C++98 only]
Конструкторы для режима C++98.
void write(unicode_t cp)
void operator()(unicode_t cp)
Выводит указанный code point согласно правилам кодирования UTF-8. Для записи отдельных байтов используется ByteSWriter::operator().
template<class ByteSWriter> utf8::writer<ByteSWriter> utf8::make_writer(ByteSWriter w)
Создаёт UTF-8 writer используя указанный ByteSWriter.
#include<__vic/utf8/writer.h> #include<__vic/swriters/string.h> #include<string> #include<vector> // C++11 using utf8_string_writer = __vic::utf8::writer<__vic::string_swriter>; // C++98 struct utf8_string_writer : __vic::utf8::writer<__vic::string_swriter> { explicit utf8_string_writer(std::string &s) : __vic::utf8::writer<__vic::string_swriter>(__vic::string_swriter(s)) {} }; std::string encode_utf8(const std::vector<__vic::unicode_t> &code_points) { std::string utf8_res; utf8_string_writer w(utf8_res); for(auto cp : code_points) w(cp); return utf8_res; }
namespace utf16 { using code_unit_t = char16_t; // since C++11 // или using code_unit_t = uint_least16_t; // C++98 } // namespace
Тип для UTF-16 code unit.
enum class utf16::status : unsigned char { ok = 0, eof, // Errors truncated_code_unit, truncated_code_point, invalid_sequence }; using utf16::status_t = utf16::status; // for C++98
Значения, возвращаемые функцией parse() класса utf16::reader.
constexpr bool utf16::is_error(utf16::status s);
Возвращает false для значений utf16::status::ok и utf16::status::eof. В остальных случаях возвращается true.
namespace utf16 { class bad_encoding; // public std::exception class truncated_code_unit; class truncated_code_point; class invalid_sequence; } // namespace
Классы исключений, бросаемые функцией read() класса utf16::reader. Все исключения порождены от абстактного базового класса utf16::bad_encoding. Эквивалентные коды статусов описаны в utf16::status.
class utf16::read_result { public: read_result(utf16::status_t s) read_result(unicode_t v); unicode_t value() const; utf16::status_t status() const; explicit operator bool() const; };
Результат чтения, возвращаемый функцией utf16::reader::parse().
read_result(utf16::status_t s)
Предусловие: s != utf16::status::okПостусловие: status() == s
unicode_t value() const
Возвращает прочитанное значение.
Предусловие: status() == utf16::status::ok
utf16::status_t status() const
Возвращает статус операции чтения.
explicit operator bool() const
То же самое, что status() == utf16::status::ok.
class utf16::read_unit_result { public: read_unit_result(utf16::status_t s) read_unit_result(utf16::code_unit_t v); utf16::code_unit_t value() const; utf16::status_t status() const; explicit operator bool() const; };
То же самое, что utf16::read_result, но для utf16::code_unit_t.
sread_result<unicode_t> utf16::convert_or_throw(utf16::read_result r);
Преобразует utf16::read_result в sread_result. Бросает исключение из __vic/utf16/exceptions.h, если is_error(r.status()).
template<class CodeUnitReader> class utf16::reader { public: using code_unit_reader_type = CodeUnitReader; CodeUnitReader &get_code_unit_reader(); const CodeUnitReader &get_code_unit_reader() const; template<class... Args> explicit reader(Args&&... args); // since C++11 reader(); // C++98 only explicit reader(CodeUnitReader r); // C++98 only utf16::read_result parse(); sread_result<unicode_t> read(); sread_result<unicode_t> operator()(); }; template<class CodeUnitReader> utf16::reader<CodeUnitReader> utf16::make_reader(CodeUnitReader r);
Вычитывает UTF-16 code points из последовательности 2-байтовых utf16::code_unit_t. Последовательность читается посредством специального reader, имеющего следующую структуру:
class CodeUnitReader
{
public:
utf16::read_unit_result operator()();
};
utf16::read_unit_result operator()()
Пытается вычитать следующий code unit. Возвращает code unit, в случае успеха, utf16::status::eof, если больше не осталось code units, либо utf16::status::truncated_code_unit если доступна только часть code unit.
CodeUnitReader &get_code_unit_reader()
const CodeUnitReader &get_code_unit_reader() const
Возвращает ссылку на используемый code unit reader.
template<class... Args>
explicit reader(Args&&... args) [C++11]
Передаёт все параметры в используемый code unit reader.
reader() [C++98 only]
explicit reader(CodeUnitReader r) [C++98 only]
Конструкторы для режима C++98.
utf16::read_result parse()
Пытается извлечь следующий code point из последовательности code unit, используя CodeUnitReader. В случае успеха возвращается прочитанный code point. Если code units кончились, возвращается utf16::status::eof. Другие значения возвращаются в случае ошибок, подробности см. в utf16::status. Для доступа к отдельным code units используется CodeUnitReader::operator().
Замечание: Функция сама по себе не бросает исключений, но их может бросать CodeUnitReader::operator().
sread_result<unicode_t> read()
sread_result<unicode_t> operator()()
То же самое, что parse(), но возвращает sread_result в случае успеха или при достижении конца файла. Бросает исключения из __vic/utf16/exceptions.h в остальных случаях.
template<class CodeUnitReader> utf16::reader<CodeUnitReader> utf16::make_reader(CodeUnitReader r)
Создаёт UTF-16 reader используя указанный CodeUnitReader.
#include<__vic/utf16/reader.h> #include<__vic/sreaders/string.h> #include<string> #include<cstdint> #include<iostream> class u16string_code_unit_reader { __vic::basic_string_sreader<char16_t> r; public: explicit u16string_code_unit_reader(const std::u16string &s) : r(s) {} __vic::utf16::read_unit_result operator()() { if(__vic::sread_result<__vic::utf16::code_unit_t> u = r()) return u.value(); return __vic::utf16::status::eof; } }; void print_utf16_code_points(const std::u16string &s) { __vic::utf16::reader<u16string_code_unit_reader> r(s); __vic::unicode_t cp; while(__VIC_SREAD_RESULT(unicode_t) cp = r()) std::cout << uint_least32_t(cp.value()) << '\n'; }
template<class CodeUnitSWriter> class utf16::writer { public: using code_unit_writer_type = CodeUnitSWriter; CodeUnitSWriter &get_code_unit_writer(); const CodeUnitSWriter &get_code_unit_writer() const; template<class... Args> explicit writer(Args&&... args); // since C++11 writer(); // C++98 only explicit writer(CodeUnitSWriter w); // C++98 only void write(unicode_t cp); void operator()(unicode_t cp); }; template<class CodeUnitSWriter> utf16::writer<CodeUnitSWriter> utf16::make_writer(CodeUnitSWriter w);
Пишет UTF-16 code points в последовательность 2-байтовых utf16::code_unit_t. Для вывода code units используется CodeUnitSWriter, моделирующий swriter<utf16::code_unit_t> (см. S-writers).
CodeUnitSWriter &get_code_unit_writer()
const CodeUnitSWriter &get_code_unit_writer() const
Возвращает ссылку на используемый code unit writer.
template<class... Args>
explicit writer(Args&&... args) [C++11]
Передаёт все параметры в используемый code unit writer.
writer() [C++98 only]
explicit writer(CodeUnitSWriter r) [C++98 only]
Конструкторы для режима C++98.
void write(unicode_t cp)
void operator()(unicode_t cp)
Выводит указанный code point согласно правилам кодирования UTF-16. Для записи отдельных code units ипользуется CodeUnitSWriter::operator().
template<class CodeUnitSWriter> utf16::writer<CodeUnitSWriter> utf16::make_writer(CodeUnitSWriter w)
Создаёт UTF-16 writer используя указанный CodeUnitSWriter.
#include<__vic/utf16/writer.h> #include<__vic/swriters/string.h> #include<string> #include<vector> std::u16string encode_utf16(const std::vector<__vic::unicode_t> &code_points) { std::u16string utf16_res; __vic::utf16::writer<__vic::basic_string_swriter<char16_t>> w(utf16_res); for(auto cp : code_points) w(cp); return utf16_res; }
class waitable_event : private non_copyable { public: explicit waitable_event(bool signaled = false); ~waitable_event(); void set(); void reset(); bool signaled() const; void wait(); bool wait_ms(unsigned msec); // BEGIN C++11 template<class Rep, class Period> bool wait_for(const std::chrono::duration<Rep,Period> &d); template<class Clock, class Duration> bool wait_until(const std::chrono::time_point<Clock,Duration> &t); // END C++11 };
Инструмент синхронизации, подобный «Event Object» в ОС Windows. Может находиться в одном из двух состояний: сигнальном или несигнальном. Поток может эффективно ждать перехода в сигнальное состояние, используя предоставляемые wait-функции (с минимальным потреблением ресурсов системы).
explicit waitable_event(bool signaled = false)
Постусловие: signaled() == signaled
bool signaled() const
Возвращает true, если объект находится в сигнальном состоянии.
void set()
Устанавливает сигнальное состояние.
Постусловие: signaled() == true
void reset()
Устанавливает несигнальное состояние.
Постусловие: signaled() == false
void wait()
Ждёт сигнального состояния без таймаута.
Постусловие: signaled() == true
bool wait_ms(unsigned msec)
Ждёт сигнального состояния не дольше указанного таймаута в миллисекундах. Returns signaled().
Замечание: В режиме C++11 используйте wait_for().
template<class Rep, class Period>
bool wait_for(const std::chrono::duration<Rep,Period> &d) [C++11]
template<class Clock, class Duration>
bool wait_until(const std::chrono::time_point<Clock,Duration> &t) [C++11]
Ждёт сигнального состояния не дольше указанного таймаута. Возвращает signaled().
class posix::cond_variable { public: constexpr cond_variable() noexcept; ~cond_variable(); cond_variable(const cond_variable & ) = delete; cond_variable &operator=(const cond_variable & ) = delete; void wait(::pthread_mutex_t &m); void wait(posix::mutex &m); bool wait_until(::pthread_mutex_t &m, const ::timespec &abstime); bool wait_until(posix::mutex &m, const ::timespec &abstime); void signal(); void broadcast(); // C++11-compatible synonyms void notify_one() { signal(); } void notify_all() { broadcast(); } };
Переменные условий POSIX.
cond_variable()
Создаёт объект.
~cond_variable()
Уничтожает объект.
void wait(::pthread_mutex_t &m)
void wait(posix::mutex &m)
Блокирует вызывающий поток и ждёт сигналов (уведомлений).
bool wait_until(::pthread_mutex_t &m, const ::timespec &abstime)
bool wait_until(posix::mutex &m, const ::timespec &abstime)
То же, что wait(), но время ожидания ограничено таймаутом. Возвращает true, если сигнал был получен, false, если таймаут истёк.
void signal()
void notify_one()
Шлёт сигнал (уведомление) одному из ожидающих потоков.
void broadcast()
void notify_all()
Шлёт сигнал (уведомление) всем ожидающим потокам.
class posix::daemon_control : private non_copyable { public: class already_running; // public std::exception explicit daemon_control(std::string pid_file_name); ~daemon_control(); void daemonize(); static int control(const char *cmd, const char *pid_file_name); static void stop_and_wait(const char *pid_file_name); };
Класс реализует функции запуска, остановки, перезапуска, контроля состояния и защиту от многократного запуска для приложений-daemon’ов. Рекомендуемый способ использования – private-наследование классом вроде application конкретного приложения.
class already_running
Исключение. Бросается конструктором, если запускаемый демон уже запущен в данный момент.
explicit daemon_control(std::string pid_file_name)
Запускает приложение и создаёт файл с именем, переданным в pid_file_name, в текущем каталоге, содержащий PID созданного процесса. Если при запуске обнаруживается, что процесс уже запущен, то бросается already_running.
~daemon_control()
Удаляет PID-файл, созданный конструктором.
void daemonize()
Делает вызывающий процесс daemon’ом и обновляет значение в PID-файле.
static int control(const char *cmd, const char *pid_file_name)
Выполняет одну из команд, заданных первым параметром, и возвращает статус:
- stop - посылка сигнала SIGTERM выполняющемуся процессу, возвращает статус успешно/неуспешно (0/1),
- kill - убийство выполняющегося процесса сигналом SIGKILL, возвращает статус успешно/неуспешно (0/1),
- status - проверка текущего состояния процесса:
- вывод «Running» в stdout и возврат статуса 0, если процесс выполняется,
- вывод «Not running» и статус 1, если не выполняется,
- вывод сообщения об ошибке в stderr и возврат статуса >1 в случае ошибок.
Если передана команда, отличная от перечисленных выше, то бросается исключение. Вторым параметром передаётся имя PID-файла, которое передавалось в конструктор при запуске.
static void stop_and_wait(const char *pid_file_name)
Посылает сигнал SIGTERM, если процесс выполняется, и дожидается его завершения. Если процесс в данный момент не выполняется, то просто возвращает управление. Функция используется для рестарта приложения. После её вызова можно создавать экземпляр daemon_control и запускать работу приложения.
class posix::dir_entries : private non_copyable { public: dir_entries(); explicit dir_entries(const char *dir); ~dir_entries(); // BEGIN C++11 dir_entries(dir_entries &&o) noexcept; dir_entries &operator=(dir_entries &&o) noexcept; // END C++11 bool reopen(const char *dir); void close(); bool is_open() const; const char *next(); #ifdef _DIRENT_HAVE_D_TYPE unsigned char type() const; #endif void rewind(); };
Класс позволяет просматривать список элементов в указанном каталоге. Специальные ссылки . и .. никогда не включаются в результирующий набор.
dir_entries()
Создаёт закрытый объект.
Постусловие: is_open() == false
explicit dir_entries(const char *dir)
Пытается открыть каталог dir для просмотра. Необходимо проверить результат открытия последующим вызовом is_open()! В случае неудачи, ошибка сохраняется в errno.
~dir_entries()
Вызывает close(), если is_open() == true.
dir_entries(dir_entries &&o) noexcept [C++11]
dir_entries &operator=(dir_entries &&o) noexcept [C++11]
Операции перемещения для режима C++11.
bool reopen(const char *dir)
Инициализирует новый листинг. Предварительно вызывает close(), если необходимо. Возвращает true в случае успеха. В случае неудачи, ошибка сохраняется в errno.
void close()
Освобождает ресурсы. Бросает исключения при ошибках.
Предусловие: is_open() == trueПостусловие: is_open() == false
bool is_open() const
Возвращает true, если next() может быть вызван.
const char *next()
Возвращает следующий элемент или nullptr, если достигнут конец списка.
Предусловие: is_open() == true
unsigned char type() const
Возвращает тип элемента: DT_REG, DT_DIR и т.п. (см. документацию на dirent в Вашей системе). Функция доступна только если определён макрос _DIRENT_HAVE_D_TYPE. Но в любом случае, в некоторых ситуациях тип не может быть определён, и возвращается DT_UNKNOWN. Все приложения должны предусматривать данную ситуацию и быть готовымив таких случаях явно получать тип вызовом stat()!
void rewind()
Перематывает курсор списка на начало.
Предусловие: is_open() == true
// Выводит содержимое текущего каталога __vic::posix::dir_entries list("."); if(!list.is_open()) __vic::throw_errno("Can't open directory for listing"); while(const char *entry = list.next()) std::cout << entry << '\n';
class posix::dir_files : private non_copyable { public: dir_files(); explicit dir_files(const char *dir, const char *pattern = nullptr); ~dir_files(); // BEGIN C++11 dir_files(dir_files &&o) noexcept; dir_files &operator=(dir_files &&o) noexcept; // END C++11 bool reopen(const char *dir, const char *pattern = nullptr); void close(); bool is_open() const; const char *next(); void rewind(); };
Класс аналогичен классу dir_entries, но игнорирует все элементы каталога, не являющиеся регулярными файлами. Также имеется возможность задания опциональной маски имён файлов с помощью параметра pattern.
// Выводит список cpp-файлов в текущем каталоге __vic::posix::dir_files files(".", "*.cpp"); if(!files.is_open()) __vic::throw_errno("Can't open directory for listing"); while(const char *file = files.next()) std::cout << file << '\n';
Инструменты обработки ошибок специфичные для POSIX.
namespace posix { bool is_EAGAIN(int err_no); bool is_ENOENT(int err_no); #define __VIC_CASE_EAGAIN case <val1>: case <val2>: ... #define __VIC_CASE_ENOENT case <val1>: case <val2>: ... }
Первая функция проверяет, имеет ли код значение EAGAIN или EWOULDBLOCK. Это работает независимо от того, имеют они одно и то же числовое значение на данной платформе или нет.
Вторая функция проверяет, имеет ли код значение ENOENT, ESTALE и им подобные, означающие, что произошла попытка обращения к несуществующему файлу.
Макросы должны использоваться внутри конструкции switch. Они раскрываются в один case с двоеточием, либо в набор case’ов, и выполняют ту же функцию, что и функции, описанные выше.
switch(errno) { __VIC_CASE_ENOENT std::cout << "No file found!\n"; break; case EPERM: ... }
class posix::file : private non_copyable { public: explicit constexpr file(int fd = -1); file(const char *name, int flags, ::mode_t mode = 0666); ~file(); // BEGIN C++11 file(file &&o) noexcept; file &operator=(file &&o) noexcept; // END C++11 bool open(const char *name, int flags, ::mode_t mode = 0666); bool is_open() const; void close(); bool close_nt() noexcept; void swap(file &o) noexcept; int detach_handle() noexcept; int attach_handle(int fd) noexcept; int handle() const; int descriptor() const { return handle(); } size_t read_max(void *buf, size_t n) { return read_max(handle(), buf, n); } size_t read_some(void *buf, size_t n) { return read_some(handle(), buf, n); } void write_all(const void *buf, size_t n) { write_all(handle(), buf, n); } size_t write_all_nt(const void *buf, size_t n) noexcept { return write_all_nt(handle(), buf, n); } static size_t read_max(int fd, void *buf, size_t n); static size_t read_some(int fd, void *buf, size_t n); static void write_all(int fd, const void *buf, size_t n); static size_t write_all_nt(int fd, const void *buf, size_t n) noexcept; static void close_reset(int &fd); static bool close_nt(int fd) noexcept; };
RAII-обёртка для POSIX'овых файловых дескрипторов, а также обёртки для системных вызовов ::read() и ::write().
explicit constexpr file(int fd = -1)
Принимает уже существующий дескриптор.
Предусловие: fd - либо открытый дескриптор, либо -1.
file(const char *name, int flags, ::mode_t mode = 0666)
Вызывает open(). Результат должен быть проверен последующим вызовом is_open().
~file()
Вызывает ::close(), если is_open() == true.
file(file &&o) noexcept [C++11]
file &operator=(file &&o) noexcept [C++11]
Операции перемещения для режима C++11.
bool open(const char *name, int flags, ::mode_t mode = 0666)
Вызывает ::open() с указанными параметрами. Возвращает is_open(). errno содержит код ошибки в случае неудачи.
bool is_open() const
Возвращает true, если файл открыт.
void close()
Безусловно вызывает ::close(). Никаких предварительных проверок, открыт ли файл, не производится! Корректно обрабатывает прерывания сигналами. Бросает исключения в случае ошибок. Постусловие соблюдается даже если функция выбросила исключение.
Предусловие: is_open() == trueПостусловие: is_open() == false
static void close_reset(int &fd)
Безусловно вызывает ::close() с указанным дескриптором и присваивает ему -1. Никаких предварительных проверок не производится! Корректно обрабатывает прерывания сигналами. Бросает исключения в случае ошибок. Постусловие соблюдается даже если функция выбросила исключение.
Предусловие: fd >= 0Постусловие: fd == -1
bool close_nt() noexcept
static bool close_nt(int fd) noexcept
Аналог close(), но не бросает исключений. В случае неудачи возвращается false, и errno содержит код ошибки.
int detach_handle() noexcept
Освобождает дескриптор из-под контроля объекта и возвращает его.
Постусловие: is_open() == false
int attach_handle(int fd) noexcept
Берёт fd под контроль и возвращает предыдущий дескриптор.
Предусловие: fd - либо открытый дескриптор, либо -1.Постусловие: handle() == fd
void swap(file &o) noexcept
Обменивается значением с o.
int handle() const
int descriptor() const { return handle(); }
Возвращает контролируемый дескриптор.
static size_t read_max(int fd, void *buf, size_t n)
size_t read_max(void *buf, size_t n) { return read_max(handle(), buf, n); }
Пытается прочитать n байт в указанный буфер. Возвращает количество прочитанных байтов. Возвращённое значение может быть меньше запрошенного только в случае достижения конца файла. Набирает вычитанные фрагменты, пока буфер не заполнится. Не прерывается сигналами. Вызывает throw_errno() в случае ошибок.
static size_t read_some(int fd, void *buf, size_t n)
size_t read_some(void *buf, size_t n) { return read_some(handle(), buf, n); }
Читает не более n байтов в указанный буфер. Возвращает количество прочитанных байтов или 0 при достижении конца файла. Функция сразу возвращает управление, как только считан первый фрагмент данных. Не прерывается сигналами. Вызывает throw_errno() в случае ошибок.
static void write_all(int fd, const void *buf, size_t n);
void write_all(const void *buf, size_t n) { write_all(handle(), buf, n); }
Записывает весь буфер в указанный дескриптор. Не прерывается сигналами. Вызывает throw_errno() в случае ошибок.
static size_t write_all_nt(int fd, const void *buf, size_t n) noexcept
size_t write_all_nt(const void *buf, size_t n) noexcept
Аналог write_all(), но не бросает исключений. Вместо них возвращает значение меньше n, errno содержит код ошибки.
__vic::posix::file file("path/file", O_WRONLY | O_CREAT); if(!file.is_open()) __vic::throw_errno("Cannot create file"); const char msg[] = "Hello"; file.write_all(msg, sizeof msg - 1); // write without null-terminator file.close();
namespace posix { struct file_stat : public ::stat { enum for_link_t { for_link }; file_stat() = default; file_stat(const struct ::stat &s); explicit file_stat(int fd); explicit file_stat(const char *path); explicit file_stat(const std::string &path); file_stat(const char *path, for_link_t); file_stat(const std::string &path, for_link_t); void get(int fd); void get(const char *path); void get(const std::string &path); void get_for_link(const char *path); void get_for_link(const std::string &path); bool try_get(int fd); bool try_get(const char *path); bool try_get(const std::string &path); bool try_get_for_link(const char *path); bool try_get_for_link(const std::string &path); bool get_if_exists(const char *path); bool get_if_exists(const std::string &path); // POSIX-defined attributes dev_t dev() const { return this->st_dev; } ino_t ino() const { return this->st_ino; } mode_t mode() const { return this->st_mode; } nlink_t nlink() const { return this->st_nlink; } uid_t uid() const { return this->st_uid; } gid_t gid() const { return this->st_gid; } off_t size() const { return this->st_size; } time_t atime() const { return this->st_atime; } time_t mtime() const { return this->st_mtime; } time_t ctime() const { return this->st_ctime; } bool is_block() const { return S_ISBLK(mode()); } bool is_char() const { return S_ISCHR(mode()); } bool is_dir() const { return S_ISDIR(mode()); } bool is_fifo() const { return S_ISFIFO(mode()); } bool is_regular() const { return S_ISREG(mode()); } bool is_link() const { return S_ISLNK(mode()); } bool is_socket() const { return S_ISSOCK(mode()); } }; inline bool is_block(const struct ::stat &s) { return S_ISBLK(s.st_mode); } inline bool is_char(const struct ::stat &s) { return S_ISCHR(s.st_mode); } inline bool is_dir(const struct ::stat &s) { return S_ISDIR(s.st_mode); } inline bool is_fifo(const struct ::stat &s) { return S_ISFIFO(s.st_mode); } inline bool is_regular(const struct ::stat &s) { return S_ISREG(s.st_mode); } inline bool is_link(const struct ::stat &s) { return S_ISLNK(s.st_mode); } inline bool is_socket(const struct ::stat &s) { return S_ISSOCK(s.st_mode); } bool is_same_file(const struct ::stat &s1, const struct ::stat &s2); }
C++ обёртка для системной структуры stat. Конструкторы и функции get() вызывают throw_errno() в случае ошибок. Функции try_get() вместо этого возвращают false, код ошибки содержится в errno.
enum for_link_t { for_link }
Тэг конструктора.
file_stat()
Создаёт неинициализированную структуру.
file_stat(const struct ::stat &s)
Копирует s.
explicit file_stat(int fd)
void get(int fd)
Получает stat для файлового дескриптора fd (системный вызов fstat()).
explicit file_stat(const char *path)
explicit file_stat(const std::string &path)
void get(const char *path)
void get(const std::string &path)
Получает stat по файловому пути (системный вызов stat()).
file_stat(const char *path, for_link_t)
file_stat(const std::string &path, for_link_t)
void get_for_link(const char *path)
void get_for_link(const std::string &path)
Получает stat для символической ссылки (системный вызов lstat()).
bool try_get(int fd)
bool try_get(const char *path)
bool try_get(const std::string &path)
bool try_get_for_link(const char *path)
bool try_get_for_link(const std::string &path)
Аналоги get() и get_for_link(), но возвращают false вместо выброса исключений.
bool get_if_exists(const char *path)
bool get_if_exists(const std::string &path)
Аналог get(), но возвращает false в случае, если файл не найден, вместо выброса исключения. Бросает исключения в случае других ошибок.
dev_t dev() const
ino_t ino() const
mode_t mode() const
nlink_t nlink() const
uid_t uid() const
gid_t gid() const
off_t size() const
time_t atime() const
time_t mtime() const
time_t ctime() const
Атрибуты структуры stat определённые стандартом POSIX.
bool is_block() const
bool is_char() const
bool is_dir() const
bool is_fifo() const
bool is_regular() const
bool is_link() const
bool is_socket() const
Обёртки стандартных макросов.
bool is_same_file(const struct ::stat &s1, const struct ::stat &s2)
Определяет, ссылаются ли структуры на один и тот же файл.
Инструменты для работы с файлами и путями на POSIX-совместимых файловых системах.
namespace posix { std::string basename(const std::string &path); std::string dirname(const std::string &path); void basename(const std::string &path, std::string &res); void dirname(const std::string &path, std::string &res); void append_basename(const std::string &path, std::string &res); void append_dirname(const std::string &path, std::string &res); }
Функции являются аналогами стандартных shell-команд. Извлекают имя файла или каталога из пути. Есть 3 варианта функций:
std::cout << __vic::posix::dirname("/usr/lib") << '\n'; // "/usr" std::cout << __vic::posix::basename("/usr/lib") << '\n'; // "lib" std::cout << __vic::posix::dirname("/usr/lib/") << '\n'; // "/usr" std::cout << __vic::posix::basename("/usr/lib/") << '\n'; // "lib" std::cout << __vic::posix::dirname("lib") << '\n'; // "." std::cout << __vic::posix::basename("lib") << '\n'; // "lib" std::cout << __vic::posix::dirname("/") << '\n'; // "/" std::cout << __vic::posix::basename("/") << '\n'; // "/" std::cout << __vic::posix::dirname("") << '\n'; // "." std::cout << __vic::posix::basename("") << '\n'; // ""
namespace posix { std::string &add_trailing_slash(std::string &path); std::string with_trailing_slash(const std::string &s); std::string with_trailing_slash(const char *s); }
Добавляют завершающий слеш к пути, если его там нет. add_trailing_slash() модифицирует свой аргумент, а with_trailing_slash() всегда создаёт новый объект и возвращает его.
std::string path = get_directory(); __vic::posix::add_trailing_slash(path) += "file.name"; // dir/file.name create_file(path);
Векторизованный ввод/вывод.
template<unsigned N> class posix::ovectors { public: ovectors(); void push_back(const void *buf, size_t buf_len); void pop_back(); ::iovec *ptr(); const ::iovec *ptr() const; unsigned size() const; bool full() const; bool empty() const; void clear(); static constexpr unsigned max_size() { return N; } size_t total_bytes() const; };
Обёртка для массива системных структур iovec, предназначенного для вывода набора разрозненных буферов со сбором (gathering). Параметр шаблона задаёт максимальное количество буферов.
ovectors()
Постусловие: empty() && total_bytes() == 0
void push_back(const void *buf, size_t buf_len)
Добавляет указанный буфер к набору.
Предусловие: !full()Постусловие: size() увеличивается на 1, total_bytes() увеличивается на buf_len.
void pop_back()
Удаляет последний добавленый в набор буфер.
Предусловие: !empty()Постусловие: size() уменьшается на 1, total_bytes() уменьшается на iov_len удалённого буфера.
::iovec *ptr()
const ::iovec *ptr() const
Возвращает указатель на массив выходных векторов.
unsigned size() const
Возвращает текущее количество векторов в массиве.
bool full() const
Возвращает size() == max_size().
bool empty() const
Возвращает size() == 0.
void clear()
Удаляет все буферы из набора.
Постусловие: empty() && total_bytes() == 0
static constexpr unsigned max_size()
Возвращает N.
size_t total_bytes() const
Возвращает суммарное количество байт во всех буферах массива.
__vic::posix::ovectors<3> v; v.push_back("ovectors", 8); v.push_back(" in work", 8); const char nl = '\n'; v.push_back(&nl, 1); // Prints: // ovectors in work __vic::posix::writev_all(1, v); // ::writev(1, v.ptr(), v.size());
namespace posix { size_t total(const ::iovec vec[], unsigned vec_len); template<unsigned vsize> size_t total(const ::iovec (&vec)[vsize]); }
Подсчитывает суммарный размер буферов в массиве vec.
void posix::cut_prefix(::iovec *&vec, unsigned &vec_len, size_t n);
Удаляет из вектора vec первые n байтов. Возвращает указатель на модифицированный набор буферов и новую длину вектора. Если n больше суммарной длины всех буферов вектора, то vec_len станет 0, значение vec при этом неопределенно.
Функция полезна для возобновления прерванного сигналом системного вызова, если записалась только часть данных.
namespace posix { void writev_all(int fd, ::iovec vec[], unsigned vec_len, size_t total); template<unsigned vsize> void writev_all(int fd, ovectors<vsize> &vec); }
Функции записывают всё содержимое вектора vec в файловый дескриптор fd. Параметр total должен содержать суммарный размер буферов вектора (может быть посчитан вызовом __vic::posix::total(vec, vec_len) или __vic::posix::total(vec)). Функция не прерывается сигналами. В случае ошибок бросаются исключения.
class posix::mutex { public: constexpr mutex() noexcept; ~mutex(); mutex(const mutex & ) = delete; mutex &operator=(const mutex & ) = delete; void lock(); bool try_lock(); bool unlock() noexcept; // System-specific handle ::pthread_mutex_t *handle(); const ::pthread_mutex_t *handle() const; };
Обёртка для простого нерекурсивного pthread_mutex_t. См. замечания по использованию в mutex.
constexpr mutex() noexcept
Создаёт незаблокированный мьютекс.
~mutex()
Уничтожает мьютекс.
void lock()
Захватывает блокировку на мьютексе. Ждёт его освобождения, если он уже захвачен другим потоком в данный момент.
bool try_lock()
Пытается захватить мьютекс. Немедленно возвращает false без ожидания, если он уже захвачен другим потоком.
bool unlock() noexcept
Освобождает ранее захваченный мьютекс. Иногда может возвращать false в случае ошибок, но в общем случае обнаружение ошибок не гарантируется.
::pthread_mutex_t *handle()
const ::pthread_mutex_t *handle() const
Возвращает системный дескриптор мьютекса.
См. posix::mutex_lock.
class posix::mutex_lock : private non_copyable, private non_heap_allocatable { public: enum adopt_t { adopt }; explicit mutex_lock(posix::mutex &mtx); mutex_lock(posix::mutex &mtx, adopt_t); explicit mutex_lock(::pthread_mutex_t &mtx); mutex_lock(::pthread_mutex_t &mtx, adopt_t); ~mutex_lock() noexcept(false); };
Управляет блокировкой на мьютексе. Снимает блокировку по окончании времени жизни объекта. Способен работать как с posix::mutex, так и с системным pthread_mutex_t.
adopt
Тэг конструктора, подавляет захват мьютекса.
explicit mutex_lock(posix::mutex &mtx)
explicit mutex_lock(::pthread_mutex_t &mtx)
Захватывает mtx.
~mutex_lock() noexcept(false)
Освобождает mtx. Может бросить исключение, если произошла ошибка и нет другого активного исключения!
mutex_lock(posix::mutex &mtx, adopt_t)
mutex_lock(::pthread_mutex_t &mtx, adopt_t)
Принимает уже захваченный mtx. См. пример.
// Типичное использование void reentrant_function() { static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; __vic::posix::mutex_lock lock(mtx); // Критическая секция до конца этого блока ... } // Использование незахватывающего конструктора __vic::posix::mutex mtx; if(mtx.try_lock()) // Пытаемся захватить мьютекс { // Мьютекс успешно захвачен using __vic::posix::mutex_lock; mutex_lock lock(mtx, mutex_lock::adopt); // Критическая секция до конца этого блока ... } else { // Мьютекс удерживается другим потоком ... }
class posix::pidfile : private non_copyable { public: class already_exists; // : public std::exception explicit pidfile(std::string file_name); ~pidfile(); void create(); void prepare_to_daemon(); void rewrite_pid(); };
Класс предназначен для работы с PID-файлами. То есть файлами, хранящими PID процесса, обычно являющегося процессом-демоном. Механизм используется для корректной остановки работы демона, а также для защиты от одновременного запуска нескольких экземпляров процесса.
class already_exists
Исключение. См. описание create().
explicit pidfile(std::string file_name)
Задаёт имя PID-файла.
~pidfile()
Удаляет PID-файл с диска.
void create()
Создаёт PID-файл с именем, указанным в конструкторе. Если такой PID-файл уже создан другим экземпляром процесса, бросает already_exists.
void prepare_to_daemon()
Данную функцию необходимо вызывать перед системным вызовом daemon().
void rewrite_pid()
Обновляет PID процесса в PID-файле. В частности PID меняется после системного вызова daemon().
Инструменты для работы с процессами ОС.
void posix::daemon(bool nochdir, bool noclose = false);
Делает вызывающий процесс UNIX-демоном. Вызов является полным аналогом одноимённого системного вызова из BSD и является просто редиректором к нему на тех системах, где вызов реализован (*BSD, Linux, QNX). В случае ошибок функция бросает исключения.
namespace posix { void ignore_signals(const int sigs[], size_t sigs_len); template<size_t Size> void ignore_signals(const int (&sigs)[Size]); // BEGIN C++11 void ignore_signals(std::initializer_list<int> sigs); // END C++11 }
Игнорировать указанные сигналы (используется вызов sigaction()).
int sigs[] = { SIGINT, SIGQUIT, SIGTERM }; __vic::posix::ignore_signals(sigs); // или в C++11 просто __vic::posix::ignore_signals({SIGINT, SIGQUIT, SIGTERM});
bool posix::process_alive(pid_t pid);
Возвращает true, если процесс с указанным PID существует и не является зомби.
class posix::sigset { public: sigset() = default; sigset(const ::sigset_t &set); template<size_t Size> sigset(const int (&sigs)[Size]); // BEGIN C++11 sigset(std::initializer_list<int> sigs); sigset &operator=(std::initializer_list<int> sigs) // END C++11 sigset &assign(const int *sigs, size_t n); template<size_t Size> sigset &operator=(const int (&sigs)[Size]); sigset &add(int signo); sigset &del(int signo); sigset &operator+=(int signo); sigset &operator-=(int signo); sigset &operator<<(int signo); sigset &operator>>(int signo); sigset &fill(); sigset &clear(); static sigset full(); static sigset empty(); bool is_member(int signo) const; bool contains(int signo) const; operator ::sigset_t &(); operator const ::sigset_t &() const; ::sigset_t *operator&(); const ::sigset_t *operator&() const; };
Удобная C++ обёртка для системного типа sigset_t. Набор операций определён в виде C++-операторов и функций-членов.
sigset()
Создаёт неинициализированный объект.
sigset(const ::sigset_t &set)
Создаёт копию set. Неявный преобразователь из типа sigset_t.
sigset(std::initializer_list<int> sigs) [C++11]
template<size_t Size> sigset(const int (&sigs)[Size])
Создаёт набор, заполненный сигналами из sigs.
sigset &operator=(std::initializer_list<int> sigs) [C++11]
template<size_t Size> sigset &operator=(const int (&sigs)[Size])
sigset &assign(const int *sigs, size_t n)
Вызывает clear() и заполняет набор сигналами из sigs.
sigset &add(int signo)
sigset &operator+=(int signo)
sigset &operator<<(int signo)
Добавляет сигнал к набору.
sigset &del(int signo)
sigset &operator-=(int signo)
sigset &operator>>(int signo)
Удаляет сигнал из набора.
sigset &fill()
Заполняет набор всеми сигналами (sigfillset()).
sigset &clear()
Очищает набор - удаляет все сигналы из набора (sigemptyset()).
static sigset full()
Создаёт полный набор сигналов.
static sigset empty()
Создаёт пустой набор сигналов.
bool is_member(int signo)
bool contains(int signo)
Проверяет, содержит ли набор signo.
operator ::sigset_t &()
operator const ::sigset_t &() const
Неявный преобразователь в sigset_t.
::sigset_t *operator&()
const ::sigset_t *operator&() const
Перегруженный оператор адреса.
using __vic::posix::sigset; namespace this_thread = __vic::posix::this_thread; // Ожидаем все сигналы, кроме SIGTERM и SIGQUIT int sig = this_thread::sigwait( sigset::full() >> SIGTERM >> SIGQUIT ); // Ждём Ctrl+C this_thread::sigwait( sigset::empty() << SIGINT ); // или в C++11 this_thread::sigwait( sigset{ SIGINT } );
C++ обёртки для Pthreads.
namespace posix::this_thread { ... }
Набор функций для управления текущим потоком.
namespace posix::this_thread { int sigwait(const ::sigset_t &set); int sigwait_at_most(const ::sigset_t &set, const ::timespec &timeout); }
sigwait() приостанавливает текущий поток до получения сигнала из набора set. Полученный сигнал возвращается в качестве результата.
sigwait_at_most() - то же самое, но ждёт не дольше указанного таймаута. Возвращает 0 в случае таймаута, либо в случае прывания сигналом не из указанного набора.
namespace posix::this_thread { ::siginfo_t sigwaitinfo(const ::sigset_t &set); bool sigwaitinfo_at_most(const ::sigset_t &set, const ::timespec &timeout, ::siginfo_t &res); }
sigwaitinfo() - аналог sigwait(), но возвращает дополнительную информацию о полученном сигнале. Сам сигнал возвращается в поле si_signo.
sigwaitinfo_at_most() - то же самое, но ждёт не дольше указанного таймаута. Возвращает false в случае таймаута, либо в случае прывания сигналом не из указанного набора. Если вернула true, то res содержит заполненную структуру siginfo_t.
namespace posix::this_thread { void block_signals(const ::sigset_t &mask, ::sigset_t *oldmask = nullptr); void unblock_signals(const ::sigset_t &mask, ::sigset_t *oldmask = nullptr); void set_sigmask(const ::sigset_t &mask, ::sigset_t *oldmask = nullptr); }
block_signals() добавляет сигналы из набора к блокируемым в текущем потоке.
unblock_signals() удаляет сигналы из набора блокируемых в текущем потоке.
set_sigmask() устанавливает маску блокируемых сигналов для текущего потока. Все сигналы из набора блокируются, остальные - нет.
Утилиты POSIX для работы со временем.
struct posix::time_spec : ::timespec { // Unit tags enum sec_units { sec }; enum msec_units { msec }; enum usec_units { usec }; enum nsec_units { nsec }; time_spec() = default; time_spec(time_t secs, long nsecs); time_spec(time_t secs, sec_units); time_spec(time_t msecs, msec_units); time_spec(time_t usecs, usec_units); time_spec(time_t nsecs, nsec_units); // BEGIN C++11 template<class Rep, class Period> time_spec(std::chrono::duration<Rep,Period> d); operator std::chrono::nanoseconds() const; template<class Rep, class Period> time_spec &operator+=(std::chrono::duration<Rep,Period> t2); template<class Rep, class Period> time_spec &operator-=(std::chrono::duration<Rep,Period> t2); // END C++11 time_spec &operator+=(time_spec t2); time_spec &operator-=(time_spec t2); }; bool operator==(time_spec t1, time_spec t2); bool operator!=(time_spec t1, time_spec t2); bool operator< (time_spec t1, time_spec t2); bool operator> (time_spec t1, time_spec t2); bool operator<=(time_spec t1, time_spec t2); bool operator>=(time_spec t1, time_spec t2); time_spec operator+(time_spec t1, time_spec t2); time_spec operator-(time_spec t1, time_spec t2);
Удобная С++ обёртка для манипуляций с системным типом timespec. Позволяет задавать время в различных единицах, а также складывать, отнимать и сравнивать значения с использованием операторов C++.
enum sec_units { sec }
enum msec_units { msec }
enum usec_units { usec }
enum nsec_units { nsec }
Тэги конструкторов для указания единиц измерения.
time_spec()
Создаёт неинициализированный объект.
time_spec(time_t secs, long nsecs)
Заполняет поля структуры указанными значениями.
time_spec(time_t secs, sec_units)
time_spec(time_t msecs, msec_units)
time_spec(time_t usecs, usec_units)
time_spec(time_t nsecs, nsec_units)
Задаёт время в секундах, миллисекундах, микросекундах и наносекундах, соответсвенно.
template<class Rep, class Period>
time_spec(std::chrono::duration<Rep,Period> d) [C++11]
Создаёт time_spec из std::chrono::duration.
operator std::chrono::nanoseconds() const [C++11]
Преобразует значение в наносекунды.
template<class Rep, class Period>
time_spec &operator+=(std::chrono::duration<Rep,Period> t2) [C++11]
time_spec &operator+=(time_spec t2)
time_spec operator+(time_spec t1, time_spec t2)
Складывает два значения.
template<class Rep, class Period>
time_spec &operator-=(std::chrono::duration<Rep,Period> t2) [C++11]
time_spec &operator-=(time_spec t2)
time_spec operator-(time_spec t1, time_spec t2)
Вычисляет разность двух значений.
Предусловие: t2 <= t1
bool operator==(time_spec t1, time_spec t2)
bool operator!=(time_spec t1, time_spec t2)
bool operator< (time_spec t1, time_spec t2)
bool operator> (time_spec t1, time_spec t2)
bool operator<=(time_spec t1, time_spec t2)
bool operator>=(time_spec t1, time_spec t2)
Операторы сравнения.
__vic::posix::time_spec t(5, __vic::posix::time_spec::sec); ::nanosleep(&t, nullptr); // sleep for 5 sec
posix::time_spec posix::nanoseconds_since_epoch();
Возвращает текущее количество секунд и наносекунд, прошедшее с наступления эпохи UNIX. Использует posix::realtime_clock::get_time() для получения значения, если он доступен. Реальная точность зависит от используемой платформы.
#if _POSIX_TIMERS > 0 class posix::realtime_clock { public: static constexpr clockid_t id = CLOCK_REALTIME; static constexpr bool is_steady = false; // BEGIN C++11 using duration = std::chrono::nanoseconds; using period = duration::period; using rep = duration::rep; using time_point = std::chrono::time_point<realtime_clock, duration>; static time_point now(); // END C++11 static posix::time_spec get_time(); }; #ifdef _POSIX_MONOTONIC_CLOCK class posix::monotonic_clock { public: static constexpr clockid_t id = CLOCK_MONOTONIC; static constexpr bool is_steady = true; // BEGIN C++11 using duration = std::chrono::nanoseconds; using period = duration::period; using rep = duration::rep; using time_point = std::chrono::time_point<monotonic_clock, duration>; static time_point now(); // END C++11 static posix::time_spec get_time(); }; #endif #endif
Системные часы POSIX (clock_gettime()) с интерфейсом часов <chrono>. Отсутсвуют в системах, которые их не поддерживают (проверяется макросом _POSIX_TIMERS). Дополнительно, monotonic_clock доступен только, если определён макрос _POSIX_MONOTONIC_CLOCK.
static constexpr clockid_t id
Системный ID часов.
static time_spec get_time()
Возвращает текущее время в виде posix::time_spec.
static time_point now() [C++11]
Возвращает текущее время в виде time_point.
template<class T>
interface sreader
{
sreader(sreader &&o); or sreader(const sreader &o);
sreader_result<T> auto operator()(); // throws on errors
};
sreader – это концепция, используемая алгоритмами библиотеки для поэлементного чтения логической последовательности элементов типа T. Является обобщением и переработкой концепции input iterator. В частности, он лучше обрабатывает потоки ввода/вывода, строки с нулевым терминатором и другие последовательности, в которых end-итератор не имеет смысла или его получение дорого. В то же время, традиционные пары итераторов [begin,end) являются просто частным случаем и полностью поддерживаются адаптером __vic::iterator_sreader.
"sreader" произносится как "S-reader", где S означает "Sequential", "Sequence" или "Stream".
В случаях, когда конкретный класс удовлетворяет требованиям данного concept для некоторого T, говорят, что он моделирует concept sreader<T>.
Каждый объект sreader должен быть move- или copy-constructible.
sreader_result<T> auto operator()()
Пытается получить следующий элемент последовательности. В случе успеха, возвращённый объект - true и его функция-член value() возвращает прочитанный элемент. Если больше элементов не осталось (EOF), возвращает false. Бросает исключения в остальных случаях.
Значения, возвращаемые sreader'ами (sreader_result), предоставляют следующий интерфейс:
template<class T>
interface sreader_result
{
explicit operator bool() const;
T value() const;
};
Результат чтения из sreader содержит статус операции чтения (успешно/неуспешно) и прочитанное значение, если чтение было успешным. Объекты sreader result (тривиально) копируемы и присваиваемы.
Реализации типов sreader result обеспечивают поддержку structured bindings в следующей форме:
auto [value, ok] = reader();
Так как в C++98 отсутсвует возможность использования спецификатора типа auto, использование разных типов для результатов непрактично. В режиме данного стандарта sreader'ы возвращают тип sread_result (или тип, неявно преобразуемый к нему).
explicit operator bool() const
Возвращает true, если чтение было успешным и value() содержит прочитанное значение. false, обычно, сигнализирует о конце файла.
T value() const
Возвращает прочитанное значение.
Предусловие: *this == true
// Basic usage (C++11+) while(auto res = reader()) { // Use res.value() ... }
// Using structured bindings (C++17+) for(;;) { auto [value, ok] = reader(); if(!ok) break; // Use value ... }
// C++26+ while(auto [value, _] = reader()) { // Use value ... }
// Legacy C++98 while(__VIC_SREAD_RESULT(T) res = reader()) { // Use res.value() of type T ... }
template<class R, class T> concept sreader = std::movable<R> && requires(R r) { {r()} -> sreader_result<T>; };
C++ concept для sreader'ов.
template<class R, class T> concept byte_sreader = sreader<R, T> && is_byte<T>::value;
C++ concept для sreader'ов читающих байты.
template<class R, class T> concept sreader_result = std::semiregular<R> && requires(R r) { bool{r}; {r.value()} -> std::same_as<T>; };
C++ concept для результатов, возвращаемых sreader.
template<class SReaderResult> struct sreader_value { typename type; }; template<class SReaderResult> using sreader_value_t = typename sreader_value<SReaderResult>::type;
Метафункция возвращающая тип, возвращаемый функцией-членом value(), для данного SReaderResult.
__vic::sreader_value<__vic::sread_result<int> >::type value; // int value;
template<class ByteSReaderResult> unsigned char uchar_value(ByteSReaderResult r);
Возвращает прочитанный байт – r.value() – в виде unsigned char.
Данная функция определена только, если is_byte<sreader_value_t<ByteSReaderResult>>::value == true.
template<class T> class sread_result { public: sread_result(); sread_result(sread_eof_t); sread_result(T v); explicit operator bool() const; T value() const; }; template<> class sread_result<unsigned char> { public: // All the members from the non-specialized version... // + Implicit converters: sread_result(sread_result<char> r); sread_result(sread_result<char8_t> r); sread_result(sread_result<std::byte> r); }; unsigned char uchar_value(sread_result<unsigned char> r); #if __cplusplus >= 201103L // C++11 #define __VIC_SREAD_RESULT(T) auto #else // C++98 #define __VIC_SREAD_RESULT(T) ::__vic::sread_result<T> #endif
Конкретный тип (шаблон), моделирующий sreader_result [C++20].
Специализация sread_result<unsigned char> соместима (неявно преобразуема) с любыми представлениями результата, представляющим байт, включая:
__vic::sread_result<char> char_reader(); __vic::sread_result<unsigned char> res = char_reader(); // OK
Макрос __VIC_SREAD_RESULT(T) используется в качестве типа возвращаемого значения в коде, который должен быть совместим с C++98, вместо auto:
__VIC_SREAD_RESULT(char) result = chars_reader();
sread_result()
sread_result(sread_eof_t)
Постусловие: !*this
sread_result(T v)
Постусловие: *this && value() == v
explicit operator bool() const
Возвращает статус операции чтения.
T value() const
Возвращает прочитанное значение.
Предусловие: *this == true
unsigned char uchar_value(sread_result<unsigned char> r)
Возвращает r.value().
Предусловие: r == true
struct sread_eof_t; inline constexpr sread_eof_t sread_eof;
Специальное значение, используемое для (неявного) конструирования неуспешного sread_result для любого типа значений.
#include<__vic/sreaders/iterator.h> template<class InputIterator, class T = typename std::iterator_traits<InputIterator>::value_type> class iterator_sreader { public: iterator_sreader(InputIterator begin, InputIterator end); sread_result<T> operator()(); InputIterator position() const; }; template<class InputIterator> iterator_sreader<InputIterator> make_iterator_sreader(InputIterator begin, InputIterator end); template<class T, class InputIterator> iterator_sreader<InputIterator,T> make_iterator_sreader_for(InputIterator begin, InputIterator end);
Адаптер для традиционной [begin,end) пары итераторов.
Дополнительная функция position() возвращает текущую позицию итератора внутри диапазона.
Может быть создан с помощью конструктора или одной из функций make_....
#include<__vic/sreaders/iterator.h> template<class InputIterator, class T = typename std::iterator_traits<InputIterator>::value_type> class iterator_sreader_n { public: iterator_sreader_n(InputIterator begin, size_t n); sread_result<T> operator()(); InputIterator position() const; }; template<class InputIterator> iterator_sreader_n<InputIterator> make_iterator_sreader_n(InputIterator begin, size_t n); template<class T, class InputIterator> iterator_sreader_n<InputIterator,T> make_iterator_sreader_n_for(InputIterator begin, size_t n);
Адаптер для итератора со счётчиком элементов.
Дополнительная функция position() возвращает текущую позицию итератора внутри диапазона.
Может быть создан с помощью конструктора или одной из функций make_....
#include<__vic/sreaders/container.h> template<class Cont, class T = typename Cont::value_type> class container_sreader { public: explicit container_sreader(const Cont &c); sread_result<T> operator()(); typename Cont::const_iterator position() const; }; template<class Cont> container_sreader<Cont> make_container_sreader(const Cont &c); template<class T, class Cont> container_sreader<Cont,T> make_container_sreader_for(const Cont &c);
Адаптер для контейнеров в стиле STL, имеющих члены begin() и end().
Дополнительная функция position() возвращает текущую позицию итератора внутри диапазона.
Может быть создан с помощью конструктора или одной из функций make_....
#include<__vic/sreaders/cstring.h> template<class charT> class basic_cstring_sreader { public: explicit basic_cstring_sreader(const charT *s); sread_result<charT> operator()(); const charT *position() const; }; using cstring_sreader = basic_cstring_sreader<char>; template<class charT> basic_cstring_sreader<charT> make_cstring_sreader(const charT *s);
Адаптер для C-строк с нулевым терминатором.
Дополнительная функция position() возвращает текущую позицию указателя внутри строки.
Может быть создан с помощью конструктора или функции make_....
#include<__vic/sreaders/string.h> template<class charT> class basic_string_sreader { public: template<class Tr, class Al> explicit basic_string_sreader(const std::basic_string<charT,Tr,Al> &s); sread_result<charT> operator()(); const charT *position() const; }; using string_sreader = basic_string_sreader<char>; template<class charT, class Tr, class Al> basic_string_sreader<charT> make_string_sreader(const std::basic_string<charT,Tr,Al> &s);
Адаптер для std::basic_string.
Дополнительная функция position() возвращает текущую позицию указателя внутри строки.
Может быть создан с помощью конструктора или функции make_....
#include<__vic/sreaders/cstream.h> class cstream_sreader { public: explicit cstream_sreader(std::FILE *fp); sread_result<char> operator()() { return __vic::read(fp); } }; cstream_sreader make_cstream_sreader(std::FILE *fp);
Моделирует sreader<char> для std::FILE.
Может быть создан с помощью конструктора или функции make_....
template<class T>
class swriter
{
public:
swriter(swriter &&o); or swriter(const swriter &o);
void operator()(T v); // throws on errors
};
swriter – это концепция, используемая алгоритмами библиотеки для поэлементной записи логической последовательности элементов типа T. Является обобщением и переработкой концепции output iterator. В частности, он лучше обрабатывает потоки ввода/вывода и другие последовательности, в которых end-итератор не имеет смысла или его получение дорого. В то же время, традиционные выходные итераторы являются частным просто случаем и полностью поддерживаются адаптером __vic::iterator_swriter.
"swriter" произносится как "S-writer", где S означает "Sequential", "Sequence" или "Stream".
В случаях, когда конкретный класс удовлетворяет требованиям данного concept для некоторого T, говорят, что он моделирует concept swriter<T>.
Каждый экземпляр класса должен быть move- или copy-constructible.
void operator()(T v)
Записывает элемент или бросает исключение в случае ошибки.
template<class W, class T> concept swriter = std::movable<W> && requires(W w, const T v) { w(v); };
C++ concept для swriter'ов.
#include<__vic/swriters/null.h> class null_swriter { public: template<class T> void operator()(T v) {} }; null_swriter make_null_swriter();
Фиктивный writer, принимающий любые значения и никуда их не выводящий (как /dev/null в UNIX).
#include<__vic/swriters/push_back.h> template<class Cont, class T = typename Cont::value_type> class push_back_swriter { public: explicit push_back_swriter(Cont &c); void operator()(T v) { c->push_back(v); } }; template<class Cont> push_back_swriter<Cont> make_push_back_swriter(Cont &c); template<class T, class Cont> push_back_swriter<Cont,T> make_push_back_swriter_for(Cont &c);
Адаптер. Использует функцию-член push_back() для записи элементов. Может быть создан с помощью конструктора или одной из функций make_....
#include<__vic/swriters/iterator.h> template<class OutputIterator, class T = typename std::iterator_traits<OutputIterator>::value_type> class iterator_swriter { public: explicit iterator_swriter(OutputIterator it); void operator()(T v); }; template<class OutputIterator> iterator_swriter<OutputIterator> make_iterator_swriter(OutputIterator it); template<class T, class OutputIterator> iterator_swriter<OutputIterator,T> make_iterator_swriter_for(OutputIterator it);
Записывает элементы в выходной итератор. Может быть создан с помощью конструктора или одной из функций make_....
#include<__vic/swriters/string.h> template< class charT, class Tr = std::char_traits<charT>, class Al = std::allocator<charT> > class basic_string_swriter { public: explicit basic_string_swriter(std::basic_string<charT,Tr,Al> &s); void operator()(charT ch); }; using string_swriter = basic_string_swriter<char>; template<class charT, class Tr, class Al> basic_string_swriter<charT,Tr,Al> make_string_swriter(std::basic_string<charT,Tr,Al> &s);
Адаптер для std::basic_string. Может быть создан с помощью конструктора или функции make_....
#include<__vic/swriters/cstream.h> class cstream_swriter { public: explicit cstream_swriter(std::FILE *fp); void operator()(char ch) { __vic::write(fp, ch); } }; cstream_swriter make_cstream_swriter(std::FILE *fp);
Моделирует swriter<char> для std::FILE. Может быть создан с помощью конструктора или функции make_....
Библиотека предоставляет инструменты для разбора и чтения конфигурационных файлов. Инструменты представляют собой:
Файл конфигурации представляет собой текстовый файл с набором параметров и их значений. Параметры бывают:
Также с точки зрения кратности и атомарные, и составные параметры могут быть:
Все параметры имеют имя и значение. Атомарные параметры определяются в виде:
имя: значение
У списков значение состоит из набора значений одного типа. Для определения списков предусмотрены две равноправные нотации. Можно использовать ту, которая кажется удобнее.
Вариант 1:
имя_списка: значение1 имя_списка: значение2 имя_списка: значение3
Вариант 2:
имя_списка { значение1 значение2 значение3 }
Как видно, первый вариант ничем не отличается по виду от определения скалярного параметра. Отличие лишь в том, что после обработки скалярный параметр получит только последнее значение, а список будет включать их все.
Формы задания одного параметра можно комбинировать. Например, если в качестве значения элемента списка нужно использовать символ }, то его можно добавить в список используя первую форму с двоеточием. Остальные элементы при этом могут быть заданы в фигурных скобках:
chars { $ # { } chars: }
Такой фрагмент создаст создаст список из четырёх элементов: «$», «#», «{», «}».
Если параметр имеет значение из нескольких строк, то для его задания следует использовать следующий синтаксис:
имя <<EOF строка 1 строка 2 ... EOF
Где EOF – это любая последовательность латинских букв. Синтаксис и логика обработки аналогичны конструкции «here-document» из Bourne Shell.
Составные параметры состоят из набора подпараметров. Например:
составной_параметр ( скалярный_подпараметр: значение списочный_подпараметр { значение1 значение2 } составной_подпараметр ( ... ) )
Как было сказано выше, составные параметры так же могут быть списочными. Второй вариант записи списков (см. выше) для составных параметров будет выглядеть следующим образом:
список_составных_параметров { ( подпараметр: значение1 ... ) ( подпараметр: значение2 ... ) }
Первый вариант записи списков для составных параметров также разрешён.
Строки, начинающиеся с символа '#' считаются комментариями и игнорируются парсером.
Если несколько файлов содержат пересекающийся набор параметров и их значений, то общую часть можно вынести в отдельный файл, а затем просто включать его в исходные файлы. Синтаксис используется следующий:
... <common.cfg> param: value ...
common.cfg здесь – это имя включаемого файла. Набор параметров из него будет включён в текущий конфигурационный файл.
В программе конфигурация представляется в виде структуры с набором полей, соответсвующих конфигурационным параметрам. Имя поля может не совпадать с именем параметра в файле. При создании экземпляра такой структуры все поля должны заполняться какими-либо значениями по умолчанию.
Составные параметры также являются структурами и могут рассматриваться как самостоятельные конфиги. По сути, они отличаются только тем, что занимают не весь файл, а только регион между круглыми скобками.
Для заполнения таких структур значениями из файла создан базовый класс config::parser [C++11]. Чтобы использовать парсер, нужно породить от него класс, в конструкторе описав перечень полей, которые мы хотим заполнять и файла. Описать атомарный параметр можно двумя способами: макросами __VIC_REGISTER_CONFIG_PARAM()/__VIC_REGISTER_CONFIG_PARAM_VP(), либо функцией-членом класса config::parser – register_param(). Первый способ удобнее использовать, если имя параметра в файле совпадает с именем поля в структуре, так как нет нужды указывать это имя дважды. Составные параметры описываются макросом __VIC_REGISTER_COMPLEX_CONFIG_PARAM(), либо вызовом функции-члена register_complex_param().
Пример. В программе имеется такая структура с конфигурационными параметрами:
struct myconfig { myconfig() = default; // заполняет поля значениями по умолчанию int int_param = 0; std::string str_param{"default"}; std::vector<std::string> list_param; };
Пусть все параметры, кроме str_param, имеют такие же имена в файле, а str_param в файле называется «string». Определяем парсер для нашей структуры:
struct myconfig_parser : public __vic::config::parser { explicit myconfig_parser(myconfig &cfg) { __VIC_REGISTER_CONFIG_PARAM(cfg, int_param); register_param(cfg.str_param, "string_param"); __VIC_REGISTER_CONFIG_PARAM(cfg, list_param); } };
Далее нам просто нужно создать экземпляр парсера и натравить его на файл функцией parse():
myconfig cfg; myconfig_parser parser(cfg); parser.parse("conf/my.cfg");
Теперь мы имеем заполненную из файла структуру cfg.
Для каждого составного параметра нужно определить свой config::parser и указать его при вызове register_complex_param().
Тип конфигурационного параметра определяется его типом в программе, и его значение проверяется на допустимость при разборе. Библиотека имеет встроенную поддержку некоторых типов:
Включением дополнительных заголовочных файлов также добавлется поддержка:
А также списков с элементами перечисленных выше типов (и любых других поддерживаемых):
Если Вы используете какой-то неподдерживаемый тип в качестве типа параметра, то можете добавить его поддерку сами. Для этого нужно всего лишь определить специализацию шаблона config::value<> для Вашего типа в следующем виде:
namespace __vic { namespace config { ///////////////////////////////////////////////////////////////////////////// template<> struct value<T> { static bool parse(const std::string &s, T &res) { // TODO: parse s } }; ///////////////////////////////////////////////////////////////////////////// }}
T – это Ваш тип. Функция parse() принимает на вход строковое представление значения из файла. Она должна проанализировать его, преобразовать в значение типа T и сохранить результат по ссылке, передаваемой вторым параметром. В случае, если значение преобразовать не получается, функция должна вернуть false.
Пример для типа bool:
bool __vic::config::value<bool>::parse(const std::string &v, bool &res) { if(v == "1" || __vic::ascii::equal_icase(v, "true") || __vic::ascii::equal_icase(v, "yes")) res = true; else if(v == "0" || __vic::ascii::equal_icase(v, "false") || __vic::ascii::equal_icase(v, "no")) res = false; else return false; // error return true; // OK }
Вообще говоря, парсер параметра не обязан быть специализацией config::value. Возможны ситуации, когда два параметра имеют один и тот же тип, но разное представление в файле, поэтому должны разбираться разными парсерами. Такие возможности предоставляются библиотекой.
Например, мы хотим задавать размер в конфигурационном файле в мегабайтах и гигабайтах, для чего будем использовать суффиксы M и G:
max_log_file_size: 2M
Очевидно, что если просто задать тип параметра как unsigned и зарегистрировать параметр в конструкторе парсера файла, то вызовется парсер config::value<unsigned> и вернёт ошибку, так как в числе встретит букву. Выходом является написание своего парсера для параметра. Вот пример его реализации:
struct file_size_value { static bool parse(const std::string &st, unsigned &res) { // разбор строки и сохранение значения... } };
Теперь нужно сказать парсеру файла, чтобы для нашего параметра использовался именно этот парсер. Делается это так:
register_param<file_size_value>(cfg.max_log_file_size, "max_log_file_size"); // или __VIC_REGISTER_CONFIG_PARAM_VP(cfg, max_log_file_size, file_size_value);
Всё. Теперь параметр будет разбираться корректно.
Аргументом шаблона register_param<> может быть либо класс, либо шаблон с одним параметром-типом. В последнем случае в качестве типа будет использован тип конфигурационного параметра.
Если неподдерживаемый тип параметра является списком значений, то для него требуется определить специализацию шаблона config::list_traits [C++11]. Вот пример добавления нового типа контейнера для хранения списка значений. Позволим, например, использовать тип std::set.
template<class T> struct list_traits<std::set<T>> { using value_type = T; static void push(std::set<T> &c, T &&v) { c.insert(std::move(v)); } };
Теперь все параметры типа std::set<T> будут автоматически считаться списочными.
class config::parser { public: class error : public std::exception {}; class source_error : public error { public: unsigned line() const; }; class duplicate_param : public std::exception {}; parser(const parser & ) = delete; parser &operator=(const parser & ) = delete; void parse(const char *file_path); void parse(const std::string &file_path); protected: parser(); ~parser(); template<class T> void register_param(T &v, const char *name); template<template<class > class ValueParser, class T> void register_param(T &v, const char *name); template<class ValueParser, class T> void register_param(T &v, const char *name); template<class Parser, class T> void register_complex_param(T &v, const char *name); #define __VIC_REGISTER_CONFIG_PARAM(c,p) this->register_param((c).p, #p) #define __VIC_REGISTER_CONFIG_PARAM_VP(c,p,vp) \ this->register_param<vp>((c).p, #p) #define __VIC_REGISTER_COMPLEX_CONFIG_PARAM(c,p,cp) \ this->register_complex_param<cp>((c).p, #p) };
Базовый класс для создания парсеров конкретных файлов и составных параметров.
class error
Базовый тип исключений, бросаемых при работе парсера.
class source_error
Исключение, бросаемое в случае неправильного формата конфигурационного файла.
class duplicate_param
Исключение, бросаемое функциями register_param() при попытке повторной регистрации параметра с таким же именем.
void parse(const char *file_path)
void parse(const std::string &file_path)
Чтение и разбор конфигурации из файла по указанному пути.
template<class ValueParser, class T> void register_param(T &v, const char *name)
Регистрация параметра с именем name, находящегося по ссылке v. Для разбора его значения будет использован парсер ValueParser.
template<template<class > class ValueParser, class T> void register_param(T &v, const char *name)
Вызывает register_param< ValueParser<T> >(v, name).
template<class T> void register_param(T &v, const char *name)
Вызывает register_param< config::value<T> >(v, name).
__VIC_REGISTER_CONFIG_PARAM(cfg, param)
Вызывает register_param(cfg.param, #param), что позволяет не указывать имя параметра дважды, если оно совпадает с именем поля в структуре программы.
__VIC_REGISTER_CONFIG_PARAM_VP(cfg, papam, ValueParser)
Аналог __VIC_REGISTER_CONFIG_PARAM(), но дополнительно задаёт ValueParser.
template<class Parser, class T> void register_complex_param(T &v, const char *name)
Регистрация составного параметра. Parser - это наследник config::parser.
__VIC_REGISTER_COMPLEX_CONFIG_PARAM(cfg, param, Parser)
Аналог __VIC_REGISTER_CONFIG_PARAM() для register_complex_param().
template<class T> class config::value { public: static bool parse(const std::string &s, T &res); }; template<> class config::value<int>; template<> class config::value<short>; template<> class config::value<long>; template<> class config::value<long long>; template<> class config::value<signed char>; template<> class config::value<unsigned>; template<> class config::value<unsigned short>; template<> class config::value<unsigned long>; template<> class config::value<unsigned long long>; template<> class config::value<unsigned char>; template<> class config::value<bool>; template<> class config::value<std::string>; template<> class config::value<__vic::string_buffer>;
Парсер значений типа T, используемый библиотекой, если такой парсер не указан явно. Разбирает входную строку s. В случае успеха сохраняет результат в res и возвращает true. Можно создавать специализации для своих типов.
Все парсеры значений создаваемые пользователем должны иметь такой же интерфейс и поведение.
template<class List>
struct config::list_traits
{
using value_type = list-element-type;
static void push(List &list, value_type &&v);
};
Специализация данного шаблона для некоторого типа List заставляет библиотеку считать данный тип списком, а параметры данного типа списочными.
typename value_type
Задаёт тип элементов списка.
static void push(List &list, value_type &&v)
Помещает v в список (обычно в конец).
#include<__vic/config/values/std/optional.h> template<class T> class config::value<std::optional<T>>;
#include<__vic/config/values/std/optional.h> template<class T, class Parser> class config::complex_value<std::optional<T>, Parser, false>;
#include<mfisoft/config/values/ip_addr.h> template<> class config::value<__vic::ipv4_addr>;
#include<__vic/config/values/std/vector.h> template<class T> struct config::list_traits<std::vector<T>>;
#include<__vic/config/values/std/list.h> template<class T> struct config::list_traits<std::list<T>>;
#include<__vic/config/values/std/forward_list.h> template<class T> struct config::list_traits<std::forward_list<T>>;
#include<__vic/config/values/bytes.h> template<class TUInt> class config::bytes_value_parser;
Парсер для параметров, задающих размеры в байтах. Воспринимает значения в виде «2K», что означает 2 килобайта. Доступны следующие суффиксы: K – кило, M – мега, G – гига. Все суффиксы являются степенями 2 (1K = 1024).
Для сборки библиотеки требуется один из поддерживаемых компиляторов C++ и утилита GNU Make версии 3.82 или выше доступные в Вашем окружении командной строки (shell).
На текущий момент поддерживаются следующие компиляторы:
Перейдите в подкаталог src и наберите:
$ gmake
Файл библиотеки будет собран.
По умолчанию используется C++17, но используя параметр std можно задать версию явно. Например для сборки в режиме C++98 наберите:
$ gmake std=98
Доступные значения включают: 98, 11, 14, 17, 20 и 23.
Также можно вручную выбрать используемый компилятор:
$ gmake compiler=gcc
Для генерации position-independent code (PIC), который позволяет компоновать библиотеку с shared object, наберите:
$ gmake pic=1
Для сборки на FreeBSD 10+ вместо gmake может использоваться утилита bmake:
$ make -f Makefile.freebsd
После всего этого нужно скопировать получившийся файл библиотеки и содержимое подкаталога include туда, где компилятор/компоновщик смогут их найти, например в /usr/local/include и /usr/local/lib.