Данная библиотека содержит компоненты, которых мне не хватает в Стандартной библиотеке 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(const wchar_t *fname, in_t); bin_file(const wchar_t *fname, out_t); bin_file(const wchar_t *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); bool open_in(const wchar_t *fname); bool open_out(const wchar_t *fname); bool open_append(const wchar_t *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)
bin_file(const wchar_t *fname, in_t)
bin_file(const wchar_t *fname, out_t)
bin_file(const wchar_t *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 wchar_t *path); bool path_exists(const std::string &path); bool path_exists(const std::wstring &path); bool file_exists(const char *path); bool file_exists(const wchar_t *path); bool file_exists(const std::string &path); bool file_exists(const std::wstring &path); bool dir_exists(const char *path); bool dir_exists(const wchar_t *path); bool dir_exists(const std::string &path); bool dir_exists(const std::wstring &path);
path_exists() проверяет, существует ли путь в фаловой системе. Вторая и третья функции дополнительно, кроме существования, проверяют, ссылается ли путь на регулярный файл или на каталог, соответсвенно.
void mkdir(const char *path); void mkdir(const wchar_t *path); void mkdir(const std::string &path); void mkdir(const std::wstring &path); bool mkdir_if_absent(const char *path); bool mkdir_if_absent(const wchar_t *path); bool mkdir_if_absent(const std::string &path); bool mkdir_if_absent(const std::wstring &path);
Создаёт каталог. Бросает исключения в случае ошибок. mkdir_if_absent() возвращает false вместо исключения, если каталог с таким именем уже существует.
void rmdir(const char *path); void rmdir(const wchar_t *path); void rmdir(const std::string &path); void rmdir(const std::wstring &path); bool rmdir_if_exists(const char *path); bool rmdir_if_exists(const wchar_t *path); bool rmdir_if_exists(const std::string &path); bool rmdir_if_exists(const std::wstring &path);
Удаляет пустой каталог. Бросает исключения в случае ошибок. rmdir_if_exists() возвращает false вместо исключения, если каталога с таким именем не существует.
std::string get_current_dir();
Возвращает текущий рабочий каталог.
void remove_file(const char *path); void remove_file(const wchar_t *path); void remove_file(const std::string &path); void remove_file(const std::wstring &path); bool remove_file_if_exists(const char *path); bool remove_file_if_exists(const wchar_t *path); bool remove_file_if_exists(const std::string &path); bool remove_file_if_exists(const std::wstring &path); bool remove_file_nt(const char *path) noexcept; bool remove_file_nt(const wchar_t *path) noexcept; bool remove_file_nt(const std::string &path) noexcept; bool remove_file_nt(const std::wstring &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 wchar_t *src_path, const wchar_t *dest_path, bool replace = false); void copy_file( const std::string &src_path, const std::string &dest_path, bool replace = false); void copy_file( const std::wstring &src_path, const std::wstring &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 wchar_t *src_path, const wchar_t *dest_path, bool replace = false); bool copy_file_if_exists( const std::string &src_path, const std::string &dest_path, bool replace = false); bool copy_file_if_exists( const std::wstring &src_path, const std::wstring &dest_path, bool replace = false); void copy_file_replace( const char *src_path, const char *dest_path); void copy_file_replace( const wchar_t *src_path, const wchar_t *dest_path); void copy_file_replace( const std::string &src_path, const std::string &dest_path); void copy_file_replace( const std::wstring &src_path, const std::wstring &dest_path); bool copy_file_replace_if_exists( const char *src_path, const char *dest_path); bool copy_file_replace_if_exists( const wchar_t *src_path, const wchar_t *dest_path); bool copy_file_replace_if_exists( const std::string &src_path, const std::string &dest_path); bool copy_file_replace_if_exists( const std::wstring &src_path, const std::wstring &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 wchar_t *src_path, const wchar_t *dest_path); void move_file(const std::string &src_path, const std::string &dest_path); void move_file(const std::wstring &src_path, const std::wstring &dest_path); bool move_file_if_exists(const char *src_path, const char *dest_path); bool move_file_if_exists(const wchar_t *src_path, const wchar_t *dest_path); bool move_file_if_exists( const std::string &src_path, const std::string &dest_path); bool move_file_if_exists( const std::wstring &src_path, const std::wstring &dest_path); void move_file_replace(const char *src_path, const char *dest_path); void move_file_replace(const wchar_t *src_path, const wchar_t *dest_path); void move_file_replace( const std::string &src_path, const std::string &dest_path); void move_file_replace( const std::wstring &src_path, const std::wstring &dest_path); bool move_file_replace_if_exists( const char *src_path, const char *dest_path); bool move_file_replace_if_exists( const wchar_t *src_path, const wchar_t *dest_path); bool move_file_replace_if_exists( const std::string &src_path, const std::string &dest_path); bool move_file_replace_if_exists( const std::wstring &src_path, const std::wstring &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 wchar_t *src_name, const wchar_t *dest_name); void rename_file(const std::string &src_name, const std::string &dest_name); void rename_file(const std::wstring &src_name, const std::wstring &dest_name); bool rename_file_if_exists(const char *src_name, const char *dest_name); bool rename_file_if_exists(const wchar_t *src_name, const wchar_t *dest_name); bool rename_file_if_exists( const std::string &src_name, const std::string &dest_name); bool rename_file_if_exists( const std::wstring &src_name, const std::wstring &dest_name); void rename_file_replace(const char *src_name, const char *dest_name); void rename_file_replace(const wchar_t *src_name, const wchar_t *dest_name); void rename_file_replace( const std::string &src_name, const std::string &dest_name); void rename_file_replace( const std::wstring &src_name, const std::wstring &dest_name); bool rename_file_replace_if_exists( const char *src_name, const char *dest_name); bool rename_file_replace_if_exists( const wchar_t *src_name, const wchar_t *dest_name); bool rename_file_replace_if_exists( const std::string &src_name, const std::string &dest_name); bool rename_file_replace_if_exists( const std::wstring &src_name, const std::wstring &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 wchar_t *path); uintmax_t file_size(const std::string &path); uintmax_t file_size(const std::wstring &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;
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().
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 windows::Bitmap { public: Bitmap() = default; explicit Bitmap(HBITMAP h); static Bitmap CreateCompatible(HDC hdc, int w, int h); bool DeleteNT() noexcept; void Delete(); void ClearHandle(); HBITMAP Handle() const; void Handle(HBITMAP h); operator HBITMAP() const; };
C++-обёртка для Win32 API HBITMAP.
Bitmap() = default
Создаёт неинициализированное значение.
explicit Bitmap(HBITMAP h)
Постусловие: Handle() == h
static Bitmap CreateCompatible(HDC hdc, int w, int h)
Вызывает ::CreateCompatibleBitmap().
bool DeleteNT() noexcept
Вызывает ::DeleteObject() и возвращает false в случае ошибки.
void Delete()
Вызывает ::DeleteObject() и бросает исключение в случае ошибки.
void ClearHandle()
Постусловие: !Handle()
HBITMAP Handle() const
operator HBITMAP() const
Возвращает обёрнутое значение HBITMAP.
void Handle(HBITMAP h)
Постусловие: Handle() == h
class windows::CriticalSection : private non_copyable { public: CriticalSection(); explicit CriticalSection(DWORD dwSpinCount); // _WIN32_WINNT >= 0x0403 ~CriticalSection(); void Enter(); bool TryEnter(); void Leave() noexcept; ::CRITICAL_SECTION *handle(); const ::CRITICAL_SECTION *handle() const; };
C++-обёртка для CRITICAL_SECTION из Win32 API.
CriticalSection()
Вызывает ::InitializeCriticalSection().
explicit CriticalSection(DWORD dwSpinCount) [_WIN32_WINNT >= 0x0403]
Вызывает ::InitializeCriticalSectionAndSpinCount().
~CriticalSection()
Вызывает ::LeaveCriticalSection().
void Enter()
Вызывает ::EnterCriticalSection().
bool TryEnter()
Вызывает ::TryEnterCriticalSection() и возвращает результат.
void Leave() noexcept
Вызывает ::LeaveCriticalSection().
class windows::CSGuard : private non_copyable { public: enum adopt_t { adopt }; explicit CSGuard(::CRITICAL_SECTION &cs); CSGuard(::CRITICAL_SECTION &cs, adopt_t); explicit CSGuard(CriticalSection &cs); CSGuard(CriticalSection &cs, adopt_t); ~CSGuard(); };
Класс, контролирующий время жизни блокировки. Работает как с ::CRITICAL_SECTION, так и с windows::CriticalSection.
explicit CSGuard(::CRITICAL_SECTION &cs)
explicit CSGuard(CriticalSection &cs)
Вызывает ::EnterCriticalSection(&cs).
CSGuard(::CRITICAL_SECTION &cs, adopt_t)
CSGuard(CriticalSection &cs, adopt_t)
Только сохраняет ссылку на cs без вызова ::EnterCriticalSection().
~CSGuard()
Вызывает ::LeaveCriticalSection(&cs).
// Типичное использование void reentrant_function() { static __vic::windows::CriticalSection cs; __vic::windows::CSGuard guard(cs); // Критическая секция до конца этого блока ... } // Использование adopt-конструктора __vic::windows::CriticalSection cs; if(cs.TryEnter()) // Пытаемся войти в критическую секцию { // Удачно using __vic::windows::CSGuard; CSGuard guard(cs, CSGuard::adopt); // Критическая секция до конца этого блока ... } else { // Критическая секция занята ... }
C++-обёртка для Win32 GDI device context (DC).
class windows::DC { public: DC() = default; explicit DC(HDC hdc); static DC CreateCompatible(HDC hdc); bool ReleaseNT(HWND hwnd) noexcept; void Release(HWND hwnd); bool DeleteNT() noexcept; void Delete(); int GetHorzRes() const; int GetVertRes() const; HWND GetWindow() const; HGDIOBJ GetCurrentObject(UINT uObjType) const; HBRUSH GetCurrentBrush() const; HPEN GetCurrentPen() const; HFONT GetCurrentFont() const; HBITMAP GetCurrentBitmap() const; void Select(HGDIOBJ hObj); // not a region object void Select(HRGN hReg); COLORREF SetPixel(int x, int y, COLORREF c); bool MoveTo(int x, int y, POINT *p = 0); bool LineTo(int x, int y); bool PolyBezier(const POINT *lppt, DWORD cPoints); bool FillRect(const RECT &rect, HBRUSH hbr); bool BlitTo(HDC dcDest, int x, int y, int w, int h, DWORD dwRop = SRCCOPY) const; bool BlitTo(HDC dcDest, const RECT &r, DWORD dwRop = SRCCOPY) const; void ClearHandle(); HDC Handle() const; void Handle(HDC hdc); operator HDC() const; };
Обёртка общего назначения для HDC. Используйте windows::ClientDC вместо ::ReleaseDC(). Используйте windows::PaintDC вместо ::BeginPaint()/::EndPaint().
DC() = default
Создаёт неинициализированное значение.
explicit DC(HDC hdc)
Постусловие: Handle() == hdc
static DC CreateCompatible(HDC hdc)
Вызывает ::CreateCompatibleDC().
bool ReleaseNT(HWND hwnd) noexcept
Вызывает ::ReleaseDC() и возвращает false в случае ошибки.
void Release(HWND hwnd)
Вызывает ::ReleaseDC() и бросает исключение в случае ошибки.
bool DeleteNT() noexcept
Вызывает ::DeleteDC() и возвращает false в случае ошибки.
void Delete()
Вызывает ::DeleteDC() и бросает исключение в случае ошибки.
int GetHorzRes() const
Возвращает горизонтальное разрешение данного DC.
int GetVertRes() const
Возвращает вертикальное разрешение данного DC.
HWND GetWindow() const
Вызывает ::WindowFromDC() и возвращает его результат.
HGDIOBJ GetCurrentObject(UINT uObjType) const
Вызывает ::GetCurrentObject() с указанным uObjType и возвращает его результат.
HBRUSH GetCurrentBrush() const
HPEN GetCurrentPen() const
HFONT GetCurrentFont() const
HBITMAP GetCurrentBitmap() const
Вызывает ::GetCurrentObject() для соответсвующего типа объекта и возвращает его результат.
void Select(HGDIOBJ hObj)
Вызывает ::SelectObject() для объекта, не являющегося регионом, и бросает исключение в случае ошибки.
void Select(HRGN hReg)
Вызывает ::SelectObject() для объекта, являющегося регионом, и бросает исключение в случае ошибки.
COLORREF SetPixel(int x, int y, COLORREF c)
Вызывает ::SetPixel() и возвращает его результат.
bool MoveTo(int x, int y, POINT *p = 0)
Вызывает ::MoveToEx() и возвращает false в случае ошибки.
bool LineTo(int x, int y)
Вызывает ::LineTo() и возвращает false в случае ошибки.
bool PolyBezier(const POINT *lppt, DWORD cPoints)
Вызывает ::PolyBezier() и возвращает false в случае ошибки.
bool FillRect(const RECT &rect, HBRUSH hbr)
Вызывает ::PolyBezier() и возвращает false в случае ошибки.
bool BlitTo(HDC dcDest, int x, int y, int w, int h, DWORD dwRop = SRCCOPY) const
bool BlitTo(HDC dcDest, const RECT &r, DWORD dwRop = SRCCOPY) const
Вызывает ::BitBlt() и возвращает false в случае ошибки.
Замечание: ::GetLastError() может быть использована для получения расширенной информации об ошибке.
void ClearHandle()
Постусловие: !Handle()
HDC Handle() const
operator HDC() const
Возвращает обёрнутое значение HDC.
void Handle(HDC hdc)
Постусловие: Handle() == hdc
class windows::ClientDC : public windows::DC, private non_copyable { public: explicit ClientDC(HWND hwnd); ClientDC(HDC hdc, HWND hwnd); ~ClientDC(); };
RAII-обёртка. Вызывает ::ReleaseDC() в деструкторе.
explicit ClientDC(HWND hwnd)
Вызывает ::GetDC(hwnd). Бросает исключение в случае ошибки.
ClientDC(HDC hdc, HWND hwnd)
Заворачивает hdc для последующего освобождения в деструкторе.
Предусловие: hdc - валидный дескриптор, возвращённый вызовом ::GetDC(hwnd).Постусловие: Handle() == hdc
~ClientDC()
Вызывает ::ReleaseDC().
class windows::PaintDC : public windows::DC, public PAINTSTRUCT, private non_copyable { public: explicit PaintDC(HWND hwnd); ~PaintDC(); };
Обёртка для PAINTSTRUCT и вызовов ::BeginPaint()/::EndPaint().
explicit PaintDC(HWND hwnd)
Вызывает ::BeginPaint(hwnd, this). Бросает исключение в случае ошибки.
~PaintDC()
Вызывает ::EndPaint().
Специфичные для Windows инструменты обработки ошибок.
class windows::error : public std::exception { public: explicit error(DWORD err_code = ::GetLastError()); explicit error(const char *prompt, DWORD err_code = ::GetLastError()); const char *what() const noexcept; DWORD code() const; };
Обёртка для системных кодов ошибок Windows, возвращаемых функцией Win32 API GetLastError(). См. libc_error для справки.
class windows::Event : private non_copyable { public: explicit Event(bool bManualReset, bool bInitialSignaled = false, LPCWSTR lpName = nullptr); ~Event(); void Set(); void Reset(); bool Wait(DWORD timeout = INFINITE) const; bool wait_for(std::chrono::milliseconds ms) const; // C++11 HANDLE handle() const; };
Обёртка для Win32 API event synchronization object.
explicit Event(bool bManualReset, bool bInitialSignaled = false, LPCWSTR lpName = nullptr)
Создаёт объект используя ::CreateEvent(). Бросает исключения при ошибках.
~Event()
Уничтожает объект.
void Set()
Вызывает ::SetEvent(). Бросает исключения при ошибках.
void Reset()
Вызывает ::ResetEvent(). Бросает исключения при ошибках.
bool Wait(DWORD timeout = INFINITE) const
Вызывает ::WaitForSingleObject(). Бросает исключения при ошибках.
bool wait_for(std::chrono::milliseconds ms) const [C++11]
Обёртка Wait(), адаптированная для chrono.
HANDLE handle() const
Возвращает Win32 API handle.
class windows::FindFile : public WIN32_FIND_DATA, private non_copyable { public: FindFile(); ~FindFile(); bool FindFirst(LPCTSTR filename); bool FindNext(); bool IsOpen() const; void Close(); bool CloseNT() noexcept; };
Обёртка для функций Win32 API FindFirstFile()/FindNextFile(). Специальные элементы . и .. никогда не включаются в результирующий набор.
FindFile()
Постусловие: IsOpen() == false
~FindFile()
Вызывает CloseNT(), если IsOpen() == true.
bool FindFirst(LPCTSTR filename)
Вызывает FindFirstFile(). Возвращает false, если таких файлов не найдено. Бросает windows::error в случае других ошибок.
Предусловие: IsOpen() == false
bool FindNext()
Вызывает FindNextFile(). Возвращает false, если больше таких файлов нет. Бросает windows::error в случае других ошибок.
Предусловие: IsOpen() == true
bool Close()
Закрывает поисковый дескриптор. Бросает windows::error в случае ошибок.
Предусловие: IsOpen() == trueПостусловие: IsOpen() == false
bool CloseNT() noexcept
То же самое, что Close(), но возвращает false вместо того, чтобы бросать исключения.
Предусловие: IsOpen() == trueПостусловие: IsOpen() == false
class windows::Handle { public: Handle() = default; // uninitialized constexpr Handle(HANDLE h); void Close(); bool CloseNT() noexcept; static void Close(HANDLE h); bool Wait(DWORD timeout = INFINITE) const; bool IsInvalid() const; void SetInvalid(); operator HANDLE() const; void swap(Handle &o) noexcept; };
C++-обёртка для Win32 API HANDLE.
Handle() = default
Создаёт неинициализированное значение.
constexpr Handle(HANDLE h)
Неявный преобразователь из HANDLE.
Постусловие: *this == h
operator HANDLE() const
Неявный преобразователь в HANDLE.
bool IsInvalid() const
Возвращает true если хранит значение INVALID_HANDLE_VALUE.
void SetInvalid()
Присваивает значение INVALID_HANDLE_VALUE.
Постусловие: IsInvalid() == true
void Close()
static void Close(HANDLE h)
Вызывает ::CloseHandle(). Бросает исключения при ошибках.
Предусловие: То же, что и для ::CloseHandle().
bool CloseNT() noexcept
Вызывает ::CloseHandle() и возвращает true в случае успеха. В случае ошибки ::GetLastError() может быть использована для получения описания ошибки.
Предусловие: То же, что и для ::CloseHandle().
bool Wait(DWORD timeout = INFINITE) const
Вызывает ::WaitForSingleObject() с указанным таймаутом. Возвращает false в случае WAIT_TIMEOUT. Бросает исключения при ошибках. windows::WaitAbandoned бросается в случае WAIT_ABANDONED.
void swap(Handle &o) noexcept
Обменивается значением с o.
struct windows::WaitAbandoned : public std::exception { const char *what() const noexcept; };
Исключение, бросаемое __vic::windows::Handle::Wait().
class windows::ShadowDC : public windows::DC, private non_copyable { public: ShadowDC(); ~ShadowDC(); void Create(HWND hwnd); void Create(HWND hwnd, int w, int h); void Destroy(); bool IsCreated() const; };
Виртуальный DC, расположенный в памяти.
ShadowDC()
Постусловие: !IsCreated()
~ShadowDC()
Вызывает Destroy(), если IsCreated() == true.
void Create(HWND hwnd)
Создаёт теневой DC, совместимый с указанным окном и тех же размеров. Бросает исключения в случае ошибок.
Предусловие: !IsCreated()Постусловие: IsCreated() == true
void Create(HWND hwnd, int w, int h)
Создаёт теневой DC указанных размеров, совместимый с указанным окном. Бросает исключения в случае ошибок.
Предусловие: !IsCreated()Постусловие: IsCreated() == true
void Destroy()
Уничтожает теневой DC.
Предусловие: IsCreated() == trueПостусловие: !IsCreated()
bool IsCreated() const
Возвращает true, если объект имеет ассоциированный теневой DC.
[[noreturn]] void throw_last_error(const char *prompt); [[noreturn]] void throw_last_error(const char *prompt, DWORD code);
Бросает исключение, содержащее глобальное значение GetLastError() или указанный code, соответственно.
if(!CloseHandle(h)) __vic::windows::throw_last_error("CloseHandle");
[[noreturn]] void throw_wsa_error(const char *prompt); [[noreturn]] void throw_wsa_error(const char *prompt, int code);
Аналог windows::throw_last_error(), но для кодов ошибок возвращаемых WSAGetLastError().
class windows::WaitCursor : private non_copyable { public: explicit WaitCursor(HCURSOR h); ~WaitCursor(); static HCURSOR GetDefault(); };
RAII-обёртка. Временно заменяет курсор мыши на курсор, переданный в конструкторе. Деструктор восстанавливает предыдущий курсор.
explicit WaitCursor(HCURSOR h)
Меняет текущий курсор на h.
~WaitCursor()
Восстанавливает курсор.
static HCURSOR GetDefault()
Возвращает wait-cursor, используемый по умолчанию (IDC_WAIT).
using __vic::windows::WaitCursor; static HCURSOR hWaitCursor = WaitCursor::GetDefault(); { WaitCursor _(hWaitCursor); // здесь курсор заменяется на hWaitCursor some_long_processing(); // здесь предыдущий курсор восстанавливается }
Конвертеры UTF-8 <-> UTF-16.
class windows::wstring : public std::wstring { public: wstring() = default; explicit wstring(size_type n) { reserve(n); } wstring(const wchar_t *st); wstring(std::wstring st); operator const wchar_t *() const { return c_str(); } };
std::wstring с автоматическим преобразованием в const wchar_t *.
windows::wstring windows::utf8to16(const char *s, size_t len); #if __cpp_lib_string_view // C++17 windows::wstring windows::utf8to16(std::string_view s); #else windows::wstring windows::utf8to16(const char *s); windows::wstring windows::utf8to16(const std::string &s); #endif
Конвертер UTF-8 -> UTF-16.
std::string windows::utf16to8(const wchar_t *s, size_t len); #if __cpp_lib_string_view // C++17 std::string windows::utf16to8(std::wstring_view s); #else std::string windows::utf16to8(const wchar_t *s); std::string windows::utf16to8(const std::wstring &s); #endif
Конвертер UTF-16 -> UTF-8.
std::string &windows::utf16to8_append( const wchar_t *s, size_t len, std::string &res); #if __cpp_lib_string_view // C++17 std::string &windows::utf16to8_append(std::wstring_view s, std::string &res); #else std::string &windows::utf16to8_append(const wchar_t *s, std::string &res); std::string &windows::utf16to8_append(const std::wstring &s, std::string &res); #endif
То же самое, что windows::utf16to8(), но добавляет результат к res. Возвращает ссылку на res.
class windows::Window { public: struct Class; struct CreateParams; Window() = default; explicit Window(HWND hwnd); static void Register(WNDCLASSEXW wcl); void Create(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWndName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, void *lpParam = nullptr); void Create(const CREATESTRUCTW &cs); bool Destroy() noexcept; LRESULT SendMessage(UINT msg, WPARAM w = 0, LPARAM l = 0); void Update(); bool Show(int nWinMode); void Redraw(const RECT *rect = 0, HRGN hrgn = 0, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW); void GetRect(RECT &r) const; RECT GetRect() const; void GetClientRect(RECT &r) const RECT GetClientRect() const; void SetText(LPCWSTR st); void SetText(const char *st); int GetText(LPWSTR st, int nMax) const; void SetPos(HWND hWndAfter, int x, int y, int w, int h, UINT uFlags); void SetPos(int x, int y, int w, int h); void SetSize(int w, int h); void SetClientAreaSize(int w, int h); void MoveTo(int x, int y); void MoveToCenter(); HWND Handle() const; void Handle(HWND hwnd); operator HWND() const; };
Тонкая обёртка для Win32 API HWND.
Window() = default
Создаёт неинициализированное значение.
explicit Window(HWND hwnd)
Постусловие: Handle() == hwnd
static void Register(WNDCLASSEXW wcl)
Вызывает ::RegisterClassExW(). Бросает исключение в случае ошибки.
void Create(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWndName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, void *lpParam = nullptr)
Вызывает ::CreateWindowExW() и обновляет завёрнутое значение HWND. Бросает исключение в случае ошибки.
void Create(const CREATESTRUCTW &cs)
Вызывает Create() со значениями, указанными в in cs. Бросает исключение в случае ошибки.
bool Destroy() noexcept
Вызывает ::DestroyWindow(). Возвращает true> в случае успеха.
LRESULT SendMessage(UINT msg, WPARAM w = 0, LPARAM l = 0)
Вызывает ::SendMessage() и возвращает возвращённое значение.
void Update()
Вызывает ::UpdateWindow(). Бросает исключение в случае ошибки.
bool Show(int nWinMode)
Вызывает ::ShowWindow() и возвращает возвращённое значение.
void Redraw(const RECT *rect = 0, HRGN hrgn = 0, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW)
Вызывает ::RedrawWindow(). Бросает исключение в случае ошибки.
void GetRect(RECT &r) const
RECT GetRect() const
Вызывает ::GetWindowRect(). Бросает исключение в случае ошибки.
void GetClientRect(RECT &r) const
RECT GetClientRect() const
Вызывает ::GetClientRect(). Бросает исключение в случае ошибки.
void SetText(LPCWSTR st)
Вызывает ::SetWindowTextW(). Бросает исключение в случае ошибки.
void SetText(const char *st)
Конвертирует указанную строку из UTF-8 в UTF-16, затем вызывает SetText(). Бросает исключение в случае ошибки.
int GetText(LPWSTR st, int nMax) const
Вызывает ::GetWindowTextW() и возвращает возвращённое значение.
void SetPos(HWND hWndAfter, int x, int y, int w, int h, UINT uFlags)
Вызывает ::SetWindowPos(). Бросает исключение в случае ошибки.
void SetPos(int x, int y, int w, int h)
Устанавливает положение и размер окна. Бросает исключение в случае ошибки.
void SetSize(int w, int h)
Устанавливает размер окна. Бросает исключение в случае ошибки.
void SetClientAreaSize(int w, int h)
Устанавливает размер клиентской зоны окна. Бросает исключение в случае ошибки.
void MoveTo(int x, int y)
Перемещает окно в указанную позицию. Бросает исключение в случае ошибки.
void MoveToCenter()
Перемещает окно в центр экрана. Бросает исключение в случае ошибки.
HWND Handle() const
operator HWND() const
Возвращает обёрнутое значение HWND.
void Handle(HWND hwnd)
Постусловие: Handle() == hwnd
// Каркас Win32-API-приложения #include<__vic/windows/window.h> #include<windows.h> #include<exception> extern "C" LRESULT CALLBACK WindowFunc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } WPARAM skeleton_win32_app(int nWinMode) { using __vic::windows::Window; static wchar_t szWinClassName[] = L"TestAppClass"; Window wnd; HINSTANCE hInstance = ::GetModuleHandle(0); HICON hIcon = LoadIcon(NULL, IDI_APPLICATION); Window::Register(Window::Class(hInstance, szWinClassName, WindowFunc) .Icon(hIcon) .SmallIcon(hIcon) .Cursor(LoadCursor(0, IDC_ARROW)) .Background((HBRUSH) GetStockObject(GRAY_BRUSH)) ); wnd.Create(Window::CreateParams(hInstance, szWinClassName, L"Skeleton Win32 application")); wnd.Show(nWinMode); wnd.Update(); return __vic::windows::MessageLoop(); } int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR lpszArgs, int nWinMode) { try { return skeleton_win32_app(nWinMode); } catch(const std::exception &ex) { __vic::windows::MsgBox(ex.what(), "Application error", MB_OK | MB_ICONERROR); } return 1; }
struct windows::Window::Class : WNDCLASSEXW { Class(HMODULE hInst, LPCWSTR name, WNDPROC wndProc) { cbSize = sizeof(WNDCLASSEXW); style = 0; lpfnWndProc = wndProc; cbClsExtra = cbWndExtra = 0; hInstance = hInst; hIcon = 0; hCursor = ::LoadCursorW(0, IDC_ARROW); hbrBackground = 0; lpszMenuName = 0; lpszClassName = name; hIconSm = 0; } Class &Style(UINT v) { style = v; return *this; } Class &ClsExtra(int v) { cbClsExtra = v; return *this; } Class &WndExtra(int v) { cbWndExtra = v; return *this; } Class &Instance(HINSTANCE v) { hInstance = v; return *this; } Class &Icon(HICON v) { hIcon = v; return *this; } Class &Cursor(HCURSOR v) { hCursor = v; return *this; } Class &Background(HBRUSH v) { hbrBackground = v; return *this; } Class &MenuName(LPCWSTR v) { lpszMenuName = v; return *this; } Class &SmallIcon(HICON v) { hIconSm = v; return *this; } };
Обёртка для WNDCLASSEXW.
using __vic::windows::Windows; Window::Register(Window::Class(hInstance, WndClassName, wndProc) .Icon(hIcon) .SmallIcon(hIcon) .Cursor(::LoadCursor(nullptr, IDC_ARROW)) .Background((HBRUSH) ::GetStockObject(LTGRAY_BRUSH)) );
struct windows::Window::CreateParams : CREATESTRUCTW { CreateParams(HMODULE hInst, LPCWSTR lpClsName, LPCWSTR lpWndName = nullptr) { lpCreateParams = 0; hInstance = hInst; hMenu = 0; hwndParent = HWND_DESKTOP; cy = cx = y = x = CW_USEDEFAULT; style = WS_OVERLAPPEDWINDOW; lpszName = lpWndName; lpszClass = lpClsName; dwExStyle = 0; } CreateParams &ExtraParam(void *v) { lpCreateParams = v; return *this; } CreateParams &Instance(HINSTANCE v) { hInstance = v; return *this; } CreateParams &Menu(HMENU v) { hMenu = v; return *this; } CreateParams &Parent(HWND v) { hwndParent = v; return *this; } CreateParams &Position(int X, int Y) { x = X; y = Y; return *this; } CreateParams &Dimension(int w, int h) { cx = w; cy = h; return *this; } CreateParams &Style(DWORD v) { style = v; return *this; } CreateParams &ExStyle(DWORD v) { dwExStyle = v; return *this; } };
Обёртка для CREATESTRUCTW.
using __vic::windows::Windows; Windows wnd; wnd.Create(Window::CreateParams(hInstance, className, title).Position(x, y));
int windows::MsgBox(HWND hwnd, const wchar_t *msg, const wchar_t *title = L"", int t = MB_OK); int windows::MsgBox( const wchar_t *msg, const wchar_t *title = L"", int t = MB_OK); int windows::MsgBox(HWND hwnd, const char *msg, const char *title = "", int t = MB_OK); int windows::MsgBox( const char *msg, const char *title = "", int t = MB_OK)
Вызывает ::MessageBoxW() и возвращает возвращённое значение. Параметр hwnd равен NULL (HWND_DESKTOP), если не указан. Функции char конвертируют строки из UTF-8 в UTF-16.
WPARAM windows::MessageLoop(HWND hwnd = NULL);
Вызывает ::GetMessage() в цикле пока она возвращает ненулевое значение (пока не вычитано WM_QUIT). Затем вызывает ::TranslateMessage() и ::DispatchMessage() с вычитанным сообщением. Возвращает MSG::wParam.
bool windows::ProcessMessages(HWND hwnd = NULL);
Аналог windows::MessageLoop(), но возвращает управление при опустении очереди. Возвращает true, если может быть вызвана ещё раз, и false, если вычитано сообщение WM_QUIT.
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 или выше доступные в Вашем окружении командной строки (cmd).
На текущий момент поддерживаются следующие компиляторы:
Перейдите в подкаталог src и наберите:
C:\> gmake
Файл библиотеки будет собран.
По умолчанию используется C++17, но используя параметр std можно задать версию явно. Например для сборки в режиме C++98 наберите:
C:\> gmake std=98
Доступные значения включают: 98, 11, 14, 17, 20 и 23.
Также можно вручную выбрать используемый компилятор:
C:\> gmake compiler=gcc
В качестве альтернативы для сборки с Visual C++ и clang-cl может использоваться NMAKE вместо gmake:
C:\> nmake -f Makefile.nmake compiler=msvc
После всего этого нужно скопировать получившийся файл библиотеки и содержимое подкаталога include туда, где компилятор/компоновщик смогут их найти.