This library contains the features I'm missing in the Standard C++ library. It's a kind of my personal Boost.
Additionally, it's an abstraction layer that hides implementation details, quirks and idiosyncrasies of OS'es, compilers and the standard library implementations.
The topmost structural units of the library are headers and the library file (or archive) - lib__vic.a or __vic.lib. Usually the library file has some suffix like lib__vic14.a. All the headers are located in the __vic/ subdirectory. One should include them like this:
#include <__vic/header.h>
Where header.h is a name of the desired header.
Almost all the code is placed within __vic namespace, including other namespaces.
During program linking, the library file must be given to the linker. Example:
$ g++ -std=c++14 prog.cpp -l__vic14
The library can be built using one of the ISO C++ standards: C++98, C++11, C++14, C++17, C++20 or C++23. The standard suffix is used as a suffix for the library file (archive).
Some features require a specific minimal standard version to be available, e.g. many of them require at least C++11. These features are marked with [C++11] badge that means "C++11 or later".
Some features are available only in particular standard mode and not available otherwise. Such features are marked with [C++98 only] badge.
Detailed description of the library components is provided in the subsequent chapters. Descriptions are grouped by headers. C++23 language syntax is mainly used as a more complete and expressive one.
Misc. fundamental definitions.
Null pointer literal. Can be used instead of NULL or 0. In ISO C++ 98 mode defined as
const int nullptr = 0;
This definition allows to write C++11-style code using C++98 standard.
It is one of the few global definitions inroduced by the library. Definition can be prevented by definition __VIC_NO_NULLPTR_DEF macro before inclusion.
int *p = nullptr; pthread_create(&tid, nullptr, thread_func, nullptr);
A macro in C++98 mode, synonym for throw(). In other standard modes the definition is absent.
template<class T, size_t N> constexpr size_t array_size(T (&array)[N]);
Returns number of elements in the array. Can be used as a compile-time expression.
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; };
Inheriting of this class suppresses generation of copy constructor and copy assignment. Same as 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; };
Inheriting of this class prevents creation of the class object on a free store using operator new.
class C : private __vic::non_heap_allocatable { }; C c; // Ok. Allocation on stack C *p = new C; // Error! Attempt to allocate on heap
The header always includes this utilities in C++11 mode.
A macro for #include. Expands to the header name that contains std::swap() definition, dependig on the used language standard.
#include __VIC_SWAP_HEADER
The library provides set of compiler-independent macros that help to determine the target platform and some platform-specific traits by checking macro presence using #ifdef.
List of hardware platforms (processors):
Other macros:
Generic algorithms.
template< std::forward_iterator Iter, std::predicate<std::iter_value_t<Iter>> Pred > Iter skip_if_front(Iter begin, Iter end, Pred pred);
Skips all leading elements that match the given predicate and returns new begin iterator.
template< std::bidirectional_iterator Iter, std::predicate<std::iter_value_t<Iter>> Pred > Iter skip_if_back(Iter begin, Iter end, Pred pred);
Skips all trailing elements that match the given predicate and returns new end iterator.
Fast, compact and locale-independent tools for ASCII-characters processing. All the tools are located within __vic::ascii namespace.
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); }
Counterparts of the corresponding <cctype> functions.
constexpr char ascii::todigit(int d);
Converts integer value from 0 to 9 to the decimal digit. The result is undefined if the input value goes beyond the range.
namespace ascii { constexpr char toxdigit_upper(int d); constexpr char toxdigit_lower(int d); }
Converts integer value from 0 to 15 to the hexadecimal digit. The first one uses upper case for A-F, the latter - lower. The result is undefined if the input value goes beyond the range.
constexpr int ascii::digit_to_number(char d);
Converts the given decimal digit to the number if ascii::isdigit(d). -1 is returned otherwise.
constexpr int ascii::xdigit_to_number(char d);
Converts the given hexadecimal digit to the number if ascii::isxdigit(d). -1 is returned otherwise.
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)
Counterparts of the corresponding <cctype> functions.
constexpr char upper_to_lower(char upper)
More restricted counterpart of tolower(). The result is undefined if the argument is not an ASCII capital letter.
Precondition: ascii::isupper(upper)
constexpr char lower_to_upper(char lower)
More restricted counterpart of toupper(). The result is undefined if the argument is not an ASCII small letter.
Precondition: ascii::islower(lower)
constexpr bool ascii::equal_icase(char ch1, char ch2);
Checks equality of the two ASCII-characters ignoring the case.
ASCII-strings processing tools.
namespace ascii { char *tolower(char *str); std::string &tolower(std::string &str); }
Translates all Latin capital letters of str to the small ones. Returns str. C-string must not be nullptr!
namespace ascii { char *toupper(char *str); std::string &toupper(std::string &str); }
Translates all Latin small letters of str to the capital ones. Returns str. C-string must not be 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 }
Checks equality of two ASCII-strings ignoring the case. The pointers must not be nullptr!
Base16 encoder and decoder.
A class used as a namespace. Contains only types, constants and static functions. No objects of this class are supposed to be created.
struct base16::bad_format : public std::exception {};
Abstract base exception class.
struct base16::bad_digit : public base16::bad_format { const char *what() const noexcept; };
Exception class thrown by base16::decode() when the input sequence contains character that is not a valid HEX digit.
struct base16::bad_length : public base16::bad_format { const char *what() const noexcept; };
Exception class thrown by base16::decode() when the input sequence length is odd.
enum class base16::status { ok, invalid_length, invalid_digit }; using base16::status_t = base16::status; // for C++98
Input sequence parsing outcome status codes returned by base16::try_decode().
// Bytes -> Text template<class ByteSReader, class CharSWriter> void base16::encode_upper(ByteSReader reader, CharSWriter writer);
Encodes bytes from reader and writes the resulting characters to writer. Upper case is used for hexadecimal digits.
ByteSReader has to model sreader<unsigned char> concept. See S-readers.
CharSWriter has to model swriter<char> concept. See 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);
Same as base16::encode_upper() but lower case is used for hexadecimal digits.
// Byte -> Text template<class CharSWriter> void base16::encode_byte_lower(unsigned char byte, CharSWriter writer);
Same as base16::encode_lower() but encodes only single byte.
// Byte -> Text template<class CharSWriter> void base16::encode_byte_upper(unsigned char byte, CharSWriter writer);
Same as base16::encode_upper() but encodes only single byte.
// Text -> Bytes template<class CharSReader, class ByteSWriter> void base16::decode(CharSReader reader, ByteSWriter writer);
Decodes characters from reader and writes the resulting bytes to writer. Exception derived from base16::bad_format is thrown if the input sequence has invalid Base16 format.
CharSReader has to model sreader<char> concept. See S-readers.
ByteSWriter has to model swriter<unsigned char> concept. See 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);
Same as base16::decode() but returns base16::status different from base16::status::ok in case of invalid input sequence instead of throwing exception.
Base64 encoder and decoder.
A class used as a namespace. Contains only types, constants and static functions. No objects of this class are supposed to be created.
struct base64::bad_format : public std::exception {};
Abstract base exception class.
struct base64::bad_digit : public base64::bad_format { const char *what() const noexcept; };
Exception class thrown by base64::decode() when the input sequence contains character that is not a valid Base64 digit.
struct base64::bad_length : public base64::bad_format { const char *what() const noexcept; };
Exception class thrown by base64::decode() when the input sequence length is not a multiple of 4.
enum class base64::status { ok, invalid_length, invalid_digit }; using base64::status_t = base64::status; // for C++98
Input sequence parsing outcome status codes returned by base64::try_decode().
// Bytes -> Text template<class ByteSReader, class CharSWriter> void base64::encode(ByteSReader reader, CharSWriter writer);
Encodes bytes from reader and writes the resulting characters to writer.
ByteSReader has to model sreader<unsigned char> concept. See S-readers.
CharSWriter has to model swriter<char> concept. See 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);
Decodes characters form reader and writes the resulting bytes to writer. Exception derived from base64::bad_format is thrown if the input sequence has invalid Base64 format.
CharSReader has to model sreader<char> concept. See S-readers.
ByteSWriter has to model swriter<unsigned char> concept. See 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);
Same as base64::decode() but returns base64::status different from base64::status::ok in case of invalid input sequence instead of throwing exception.
constexpr size_t base64::encoded_length(size_t orig_len);
Calculates the length of encoded sequence of characters using the original length of the bytes sequence.
constexpr size_t base64::max_decoded_length(size_t orig_len);
Estimates the maximum length of decoded sequence of bytes using the original length of the characters sequence. The actual value depends on trailing '=' in the encoded value.
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); };
Unbuffered binary file. OS-independent wrapper for low-level system API.
Following open modes are available:
enum in_t { in }
enum out_t { out }
enum append_t { append }
Constructor tags.
bin_file()
Postcondition: 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)
Call open_in(fname), open_out(fname) or open_append(fname), correspondingly. is_open() or throw_if_closed() has to be called then to check the result.
~bin_file()
Closes the file if is_open() == true.
bin_file(bin_file &&o) noexcept [C++11]
bin_file &operator=(bin_file &&o) noexcept [C++11]
Move operations for C++11 mode.
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)
Open file for reading, writing or appending, correspondingly. Return is_open().
Precondition: is_open() == false
bool is_open() const
Returns true if file is open.
size_t read_max(void *buf, size_t n)
Tries to read n bytes to the specified buffer. Returns number of bytes read. Returned value can be less than requested only when end of file was reached. Throws on error.
Precondition: is_open() == true
size_t read_some(void *buf, size_t n)
Reads no more than n bytes to the specified buffer. Returns number of bytes read or 0 in case of end-of-file. The function returns after first chunk of any size was successfully received. Throws on error.
Precondition: is_open() == true
void write_all(const void *buf, size_t n)
Writes the whole buffer to the file. Throws on error.
Precondition: is_open() == true
void close()
Closes the file. Throws on error.
Precondition: is_open() == truePostcondition: is_open() == false
bool close_nt() noexcept
A counterpart of close() but never throws, returns false instead in case of error.
void swap(bin_file &o) noexcept
Swaps the value with o.
[[noreturn]] void throw_last_error(const char *msg)
Throws exception with the last error description if available. what() will contain msg as a substring anyway.
void throw_if_closed(const char *msg)
Calls throw_last_error(msg) if !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
Bits and bytes manipulation tools.
constexpr uint8_t lo_nibble(uint8_t byte); constexpr uint8_t hi_nibble(uint8_t byte);
Return the value of the low-order/high-order half-byte (tetrad), respectively.
template<class T> constexpr T lsb_ones(unsigned bits_num); template<class T> constexpr T msb_ones(unsigned bits_num);
Return the value of the type T with all least/most significant bits_num bits filled with 1, respectively. All other bits are set to 0.
template<class T> constexpr T get_lsbs(T v, unsigned bits_num);
Returns bits_num least significant bits of v. In other words, zeroes all but bits_num least significant bits.
constexpr int ord(char ch);
Returns the character code from 0 to 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);
Returns the number of one bits in the given value.
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);
Returns the position of the most significant 1-bit. The result is unspecified if v == 0.
template<class UInt> bool ispow2(UInt n);
Returns true if n is an integral power of 2.
template<class UInt> UInt ceil2(UInt n);
Returns the minimal value m such that ispow2(m) && m >= n. If m is not representable as a value of type UInt, the result is an unspecified value.
template<class UInt> UInt floor2(UInt n);
If n != 0 returns the maximal value m such that ispow2(m) && m <= n. Otherwise 0 is returned.
template<class UInt> unsigned ceil_log2(UInt n);
Returns ceil(log2(n)) if n != 0 or 0 otherwise.
template<class UInt> unsigned floor_log2(UInt n);
Returns floor(log2(n)) if n != 0 or 0 otherwise.
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);
The functions perform circular left (rotl) or right (rotr) bitwise shift (rotation).
constexpr uint8_t swapped_nibbles(uint8_t b);
Swaps a low-order half-byte with a high-order one and returns the value.
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;
The standard containers in C++98 don't allow to store non-copiable elements. Even in C++11 elements of containers like std::vector have to be at least noexcept movable. This class solves the problem. It is a dynamic array for non-copyable objects or just std::vector without autogrowing capacity().
Without emplace_back() it is impossible to create arbitrary new object right in the container's memory. C++98 lacks forwarding references so it is ear impossible to pass arbitrary parameters to the element's constructor. bounded_vector overcomes this problem using the following mechanism. The new element is created using several phases:
See the example at the end of the article.
Maximum capacity is specified on creation of the container. Later it can be changed but all the elements has to be destroyed before. In other words, the container can be recreated (recreate() function).
When available, emplace_back() must be used for elements creation. If not, the unsafe interface described above must be used with care. It is very ugly and error-prone but solves the task. After the element is created in the container, you operate with it almost as easy as with any other copyable object in the standard container. Anyway, it is more efficient and convenient to use in general than alternative approaches like creating the objects on the free store and placing only pointers to the container, even if we have std::unique_ptr to manage lifetime of the objects.
Fundamental differences from std::vector:
typename value_type
Type of the elements.
typename iterator
typename const_iterator
Iterators.
bounded_vector()
Create the object without memory allocation.
Postcondition: capacity() == 0
explicit bounded_vector(size_t max_size)
Allocates memory for max_size elements.
Postcondition: capacity() == max_size
~bounded_vector()
Calls clear().
bounded_vector(bounded_vector &&o) noexcept [C++11]
bounded_vector &operator=(bounded_vector &&o) noexcept [C++11]
Move operations for C++11 mode.
size_t size() const
size_t capacity() const
Current size and capacity of the container.
bool empty() const
Returns size() == 0.
bool full() const
Returns size() == capacity().
void recreate(size_t new_max_size, bool size_exact = false)
Recreates the container. At first calls clear(), then reallocates memory buffer if new_max_size > capacity() or size_exact is true and new_max_size != capacity().
Postcondition: capacity() >= new_max_size && empty() == true (if size_exact == true then capacity() == new_max_size && empty() == true)
void *alloc()
Returns the raw memory block where new instance of value_type can be allocated.
Precondition: !full()Note: Use emplace_back() in C++11 mode.
void push_allocated()
This call right after alloc() adds the just created object to the container.
template<class... Args> T &emplace_back(Args &&... args) [C++11]
Constructs new object and adds it to the container (alloc() + new + push_allocated() with a single call). A reference to the new object is returned.
Precondition: !full()
void pop_back()
Remove the last element from the container.
Precondition: !empty()
void clear()
Destroys the elements in the reverse order they were created.
Postcondition: size() == 0 (empty() == true)
void swap(bounded_vector &o)
template<class T> void swap(bounded_vector<T> &o1, bounded_vector<T> &o2) noexcept
Swaps the value with o.
T &operator[](size_t i)
const T &operator[](size_t i) const
Access to the elements by index.
Precondition: i < size()
T &front()
const T &front() const
T &back()
const T &back() const
Access to the first and the last elements.
Precondition: !empty()
iterator begin()
const_iterator begin() const
const_iterator cbegin() const
iterator end()
const_iterator end() const
const_iterator cend() const
Access to the elements via iterators.
// Creating vector for 2 objects of class C __vic::bounded_vector<C> v(2); // Creating new object in C++98 mode: new(v.alloc()) C(...); // Request memory and construct the object v.push_allocated(); // Fixate successfully created object in the container // Creating new object in C++11 mode: v.emplace_back(...);
Date and time utilies.
constexpr bool is_leap_year(int year);
Determines if the year is a leap year according to Gregorian calendar.
int days_in_month(int month, int year);
Returns number of days in the month. Month is a number from 1 to 12. The second parameter is used only if the month is 2 (february), otherwise is just ignored.
long days_between_years(unsigned year1, unsigned year2);
Returns the difference in days between the beginning of the 2nd year and the beginning of the 1st year.
class invalid_date; // : public std::exception
The exception thrown when the value of the date or time element is invalid.
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);
Checks if the date/time value is valid. Following constraints are checked:
invalid_date is thrown in case of the constraint violation.
Byte order-related utilities.
namespace endian {
enum endianness
{
unknown = 0,
little = 1234,
big = 4321,
pdp = 3412,
native = <one-of-the-above>
};
} // namespace
using endian::endianness;
Byte order constants. endian::native is set equal to one of the constants and represents the byte order used on the current platform (like __BYTE_ORDER__ macro on UNIX-like platforms). The values are supposed to be used for template specializations or for compile-time checks (e.g. in static_assert).
template<__vic::endianness > struct some_algo; // not implemented // Implementation for little-endian template<> struct some_algo<__vic::endian::little> { static void doit() { ... } }; // Implementation for big-endian template<> struct some_algo<__vic::endian::big> { static void doit() { ... } }; // Automatically choose an appropriate implementation for the used platform 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);
Return a value represented in native byte order converted from litte/big endian if appropriate.
T can be any integral type or enum with size up to 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);
Return a value represented in litte/big endian byte order.
T can be any integral type or enum with size up to 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);
Fast utilities to reverse byte order (usually implemented using compiler-specific intrinsics).
static_assert(__vic::swab32(0x01020304) == 0x04030201);
Error handling tools.
class exception : public std::exception { public: exception(); explicit exception(const char *message); const char *what() const noexcept; protected: void set_message(const char *message); };
Small extension of std::exception - the object carries message specified in the constructor, what() returns this message. Can be used either as a base or a concrete exception class. Does not use/depend on std::string as opposed to std::logic_error and std::runtime_error. You also don't have to decide which one of them you should use in the particular case.
exception()
Creates the object with an empty message.
explicit exception(const char *message)
Creates the object with the specified message.
const char *what() const noexcept
Returns the message specified before.
void set_message(const char *message)
Sets a new 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; };
This class is an easy and straightforward replacement of the standard error handling machinery used in the C-world - errno, with exceptions. The class is also suitable for usage in the multithread environment instead of not always reentrant call std::strerror().
Below you can see typical C code:
// C: int fd; if((fd = open("qqqq", O_RDONLY)) == -1) { perror("open"); if(errno == ENOENT) exit(1); }
If the file is not found, the message like this
open: No such file or directory
is printed to stderr and the program exits with the status 1.
What issues are inherent in this code? Firstly, not every program has stderr, so a library function is not allowed to print error messages there. Secondly, the value of the global variable errno can be rewritten by any subsequent call unless the value is saved explicitly right after the call. Thirdly, the decision about termination of the process can only be made by the application. An ordinary library function is not allowed to do this. Fourthly, in general case C++ program cannot call std::exit(), because destructors of the live objects allocated on the stack won't be called, and program's logic can be corrupted.
The example adapted for C++ using our class:
// 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; }
As it can be seen, the function handles erroneous situation correctly and reports it to the caller. Afterwards the caller can handle the error appropriately. In the elementary case it acts as the former C-program: prints the message to the standard error output stream and terminates. Moreover, error code is now saved in the exception and cannot be rewritten by accident.
explicit libc_error(int err_no = errno)
err_no - error code.
Postcondition: code() == err_no
explicit libc_error(const char *prompt, int err_no = errno)
prompt - a title of the error message. The parameter has the same meaning as the parameter of std::perror().
const char *what() const noexcept
Returns error description in the std::perror() format.
int code() const
int get_errno() const
Returns stored error code.
Filesystem utilities.
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() checks whether the path exists in the system. Second and third functions additionally check, besides the presence, if the path references to a regular file or to a directory, respectively.
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);
Creates a directory. Throws exception in case of failure. mkdir_if_absent() returns false instead of throwing if the directory already exists.
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);
Deletes an empty directory. Throws exception in case of failure. rmdir_if_exists() returns false instead of throwing if the directory doesn't exist.
std::string get_current_dir();
Returns current working directory.
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;
Deletes the file. Throws exception in case of failure.
remove_file_if_exists() returns false instead of throwing if the file doesn't exist.
remove_file_nt() doestn't throw any exceptions at all, false is returned in case of failure.
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);
Creates a new file dest_path which is a copy of a file src_path. If the new file exists and replace == false, the functions fail.
copy_file_if_exists() returns false instead of throwing if src_path doesn't exist.
copy_file_replace() is the same as copy_file(..., true).
copy_file_replace_if_exists() is the same as 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);
Moves a file src_path to new location specified by dest_path.
The functions with _replace suffix overwrite existing destination file if exists, others - fail in such case.
move_file_if_exists() returns false instead of throwing if src_path doesn't exist.
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);
Renames a file src_path to dest_path. The new path has to be located within the same physical filesystem.
As opposed to std::rename(), the functions without _replace suffix fail if dest_path exists.
rename_file_if_exists() returns false instead of throwing if src_path doesn't exist.
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);
Returns file size in bytes.
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>;
An IPv4 address. A handy C++-wrapper for ::in_addr.
ipv4_addr() = default
Creates an uninitialized IPv4 address.
constexpr ipv4_addr(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
Creates IPv4 address a.b.c.d.
constexpr ipv4_addr(::in_addr a)
constexpr explicit ipv4_addr(::in_addr_t a)
Converters for the system types.
static constexpr ipv4_addr any()
Returns IPv4 address 0.0.0.0.
static constexpr ipv4_addr loopback()
Returns IPv4 address 127.0.0.1.
constexpr bool is_any() const
Returns true if the address is 0.0.0.0.
constexpr bool is_loopback() const
Returns true if the address is 127.0.0.1.
static bool parse(const char *begin, const char *end, ::in_addr &res)
Parses an IPv4 address from the text representation. In case of success, returns true and res contains the parsed value.
Note: Expected input format is exactly 4 components in decimal format separated by dots.
static bool parse(std::string_view s, ::in_addr &res) [C++17]
Alternative prototype of the function with the same name for 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]
Alternative prototypes of the function with the same name before C++17.
constexpr ipv4_addr operator&(ipv4_addr a1, ipv4_addr a2)
Applies the specified mask to the specified address.
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)
Equality comparison operators.
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)
Relational comparison operators. The resulting order may differ across platforms, but always mathematically consistent within a given platform.
void to_text_append(::in_addr ip, std::string &res)
Converts the specified IPv4 address to decimal dotted notation.
template<> struct std::hash<ipv4_addr>
Specialization for 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); template<> struct std::hash<ipv6_addr>;
ipv4_addr analogue for IPv6/::in6_addr.
explicit ipv6_addr(const ::in_addr &ip4)
Creates IPv4-mapped IPv6 address.
Postcondition: is_ipv4_mapped() == true
static constexpr ipv6_addr any()
Returns IPv6 address ::.
static constexpr ipv6_addr loopback()
Returns IPv6 address ::1.
bool is_any() const
Returns true if the address is ::.
bool is_loopback() const
Returns true if the address is ::1.
bool is_ipv4_mapped() const
Returns true if the address is an IPv4-mapped IPv6 address.
ipv4_addr to_ipv4() const
Converts the IPv4-mapped IPv6 address to ipv4_addr.
Precondition: is_ipv4_mapped() == true
Iterators-related utilities.
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]);
Return a pointer to the beginning and past-the-end of the array.
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);
A counterpart of std::advance() but differs in parameters and behaviour:
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() returns an iterator advanced by n positions. prev() does the same but in reverse order. As opposed to C++11 STL functions of the same name, the offset cannot be negative. The versions with single parameter just call ++it/--it and return the result.
template<class Container> void f(const Container &c) { // Begin a traversal starting from the second element // v.begin() + 1 works only with RandomAccessIterator // ++v.begin() may cause a compile error 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
Logging front-end. Provides building of the log records using operator <<, like iostream. Each log record has the associated severity. The logger itself can filter out the records by severity. There are 7 predefined levels of the severity (in ascending order):
INFO is the default logging level but any other can be chosen. If severity of the log message (record) is below the logging level, it will be ignored and will not be published in the log's output.
For creation of messages with the required severity, the set of functions with the same name as the severity is available. For instance info() for INFO messages. Or alternatively the universal function message() can be used, in which the severity is the argument. Usually the specific functions should be used.
There are two ways of logging. The first is plain and common:
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");
The second is slightly more complex but provides more capabilities:
log.error() << "Cannot open file " << filename << '!'; log.warning() << "Loop iteration no " << i;
The call without parameters creates the object of type logger::record with the corresponding severity. Futher, the message is formed using operators <<. The message will be output to the log at the end of the full-expression (term from the Standard).
If the message can't or shouldn't be formed with a single expression, the named object of type logger::record has to be created, and parts of the message have to be written to it. The resulting message will be output to the log by it's destructor:
{ logger::record rec = log.info(); // Begin new record rec << "List elements: "; for(auto el : list) rec << el << ", "; // Constructed record will be printed to the log when the block exits }
log.info("Message"); // but not log.info() << "Message";
Output of the records with the severities DEBUG and TRACE is usually disabled. Such records will not be published in the log but the program will waste time to format it. Therefore before creating any debug message using operator << one should check if debug is enabled using debug_visible() or trace_visible() call:
if(log.debug_visible()) log.debug() << ...; // build the message
This advice doesn't cover plain calls debug(msg) and trace(msg), which have a prepared message already and don't perform any formatting.
To use logger one has to implement abstract base class logger::output (override publish_record()). The implementation has to output the passed record somewhere, e.g. to file, terminal or DB. The output specified during logger construction can be replaced later using reset_output().
severity::trace
severity::debug
severity::info
severity::notice
severity::warning
severity::error
severity::fatal
Severity constants. Use this form for both C++11 and C++98 mode.
typename severity_t
Use this identifier as a type name if your code has to be C++98-compatible. Since C++11 it is just a synonym for severity.
class output
Logging back-end interface.
void output::publish_record(severity_t sev, const char *buf, size_t buf_len)
The implementaion of this pure virtual function has to output the content of buf to the log as one record. buf_len is the length of buf. The function is always called with sev >= level(). The implementation can rely on it.
class settings_t
Keeps the logger settings: logging level and reference to output (level() + get_output()).
explicit logger(output &out, severity_t level = severity::info)
Creates logger with the specified output and logging level. The output object must outlive the logger object!
Postcondition: this->level() == level && &this->get_output() == &out
explicit logger(settings_t s)
Creates logger using the specified settings.
severity_t level() const
Returns current logging level.
void level(severity_t new_level)
Sets the logging level.
Postcondition: level() == new_level
settings_t settings() const
Returns current logging settings.
output &reset_output(output &out)
Sets new output and returns the previous one.
Postcondition: &get_output() == &out
output &get_output()
const output &get_output() const
Returns reference to the current logging output.
static constexpr size_t min_buffer_size
Minimal size of the internal buffer in bytes.
void shrink_buffer(size_t limit)
Sets the internal buffer size to min_buffer_size if it is more than limit bytes. Allows to restrict the buffer growth when long records are formed.
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]
Writes the message with the specified severity.
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]
Writes the message with the corresponding severity.
template<class... Args>
void format(severity_t s,
std::format_string<Args...> fmt, Args&&... args) [C++20]
Formats the message using the specified format string and arguments (like std::format does) then writes it with the specified severity.
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]
Formats the message using the specified format string and arguments (like std::format does) then writes it with the corresponding severity.
logger::record trace()
logger::record debug()
logger::record info()
logger::record notice()
logger::record warning()
logger::record error()
logger::record fatal()
Creates new record with the corresponding severity. Message parts can be added using operators <<.
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
Returns true if a record with the corresponding severity will be published. Usage of this functions enables to avoid formatting of the messages which eventually will not be published.
record::record(logger &log, severity_t sev)
Creates the log record with the specified severity. Usually the logger's functions like info() without parameters should be used instead.
record::~record()
Outputs the constructed record to the log.
record record::append(const char *str, size_t str_len)
Appends the string to the message.
template<class T> record record::operator<<(const T &v)
The set of inserters for various data types. The specified value is converted to text using log_value<T>::to_text() call.
template<class T> void log_value<T>::to_text(const T &v, std::string &s)
Converts a value of type T to text using unqualified to_text_append() call.
Note: This function (class with static function, to be precise) can be specialized for your T. But usually you just define 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]
Returns text label for the specified severity that can be printed to the log. For example, "DEBUG" will be returned for severity::debug.
///////////////////////////////////////////////////////////////////////////// // Output messages to std::clog with the severity label 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"); }
Output:
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
Memory-related utilities.
template<class T> T load_unaligned(const void *p);
Load value from potentially unaligned address without causing bus error (SIGBUS).
const void *p = ...; // int data = *static_cast<const int *>(p); // potential bus error int data = __vic::load_unaligned<int>(p);
template<class T> void store_unaligned(void *p, T v);
Store value to potentially unaligned address without causing bus error (SIGBUS).
void *p = ...; // *static_cast<int *>(p) = 123; // potential bus error __vic::store_unaligned(p, 123);
class mutex : private non_copyable { public: mutex(); ~mutex(); void lock(); bool try_lock(); bool unlock() noexcept; };
Plain non-recursive mutex.
In most cases explicit usage of lock() and unlock() should be avoided. Use class mutex_lock to manage locks instead. It provides exception safety and it's handy for usage.
In C++11 mode std::mutex can be a better alternative.
mutex()
Creates unlocked mutex.
~mutex()
Destroys the mutex.
void lock()
Acquires the mutex. Waits until released if acquired by other thread at the moment.
bool try_lock()
Tries to acquire the mutex. Immediately returns false if it's already acquired by another thread, without waiting.
bool unlock() noexcept
Releases the mutex acquired before. In some cases can return false in case of error, but in general error detection is not guaranteed.
See 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(); };
Manages the lock on a mutex. The lock exists while the object is alive.
adopt
Constructor tag, suppresses the mutex acquisition.
explicit mutex_lock(mutex &mtx)
Acquires mtx.
~mutex_lock()
Releases mtx.
mutex_lock(mutex &mtx, adopt_t)
Adopts already acquired mtx. See the example.
// Typical usage __vic::mutex mtx; void reentrant_function() { __vic::mutex_lock lock(mtx); // Critical section code until the end of the block ... } // Usage of non-acquiring constructor if(mtx.try_lock()) // Try to acquire the mutex { // The mutex has been successfully acquired __vic::mutex_lock lock(mtx, __vic::mutex_lock::adopt); // Critical section code until the end of the block ... } else { // The mutex is acquired by another thread ... }
Inclusion of the first file turns off struct members alignment. In other words, turns on "structures packing" - size of the struct is strictly a sum of its members sizes. Inclusion of the second one restores the default alignment. So that the pair of #include directives forms a section in the source file where structs alignment is disabled.
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> // alignment disabled starting from here 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> // alignment enabled again
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;
The simple non-mutable null-terminated string class with automatic memory management. Has simple and predictable structure that can be useful when ABI compatibility/stability is required or when usage of std::string is objectionable for some reason. The functionality provided by the class is also minimal. It provides string copying and storing and read access to it. One cannot modify string parts - only replace the whole value.
If one need to store a string value in a class, usage of this class may be a good choice. It's easier-to-use, more clear and safer than array of chars (char[]) and can be more efficient than std::string, though, of course, less universal. If modifications of the string parts are expected, usage of another string class should be considered, for instance, __vic::string_buffer. Class readonly_cstring is not designed for such purposes.
readonly_cstring()
Creates an empty string.
Postcondition: empty() == true
readonly_cstring(const char *str)
readonly_cstring(const readonly_cstring &str)
Creates a copy of str.
readonly_cstring(const char *chars, size_t n)
readonly_cstring(const char *begin, const char *end)
Creates a string from characters range.
readonly_cstring &operator=(const char *str)
readonly_cstring &operator=(const readonly_cstring &str)
readonly_cstring &assign(const char *str)
Assigns str.
readonly_cstring(readonly_cstring &&str) noexcept [C++11]
readonly_cstring &operator=(readonly_cstring &&str) noexcept [C++11]
Move operations for C++11 mode.
readonly_cstring &assign(const char *begin, const char *end)
readonly_cstring &assign(const char *chars, size_t n)
Assigns the string constructed from characters range.
bool empty() const
Returns true if string is empty.
const char *c_str() const
operator const char*() const
Returns a pointer to the stored string. The pointer is never null.
char *reserve(size_t n)
Allocates internal buffer for n chars and returns the pointer to it. Can be useful in conjunction with functions like std::sprintf().
Note: Try to avoid this unsafe function!
void swap(readonly_cstring &str) noexcept
Swaps the value with 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)
Compares two strings as std::strcmp does.
bool operator==(const readonly_cstring &s1, const readonly_cstring &s2)
...
bool operator>=(const char *s1, const readonly_cstring &s2)
Full set of comparators for readonly_cstring and const char * in all combinations.
void swap(readonly_cstring &s1, readonly_cstring &s2) noexcept
Specialization of the standard algorithm.
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(); };
Compact (only 32 bytes) and very fast implemetation of a set of chars. The cost of the contains() operation is always constant regardles of the argument and number of elements in the set.
constexpr set_of_chars()
Creates an empty set.
Postcondition: contains(char(ch)) == false for any char
template<class Iter> constexpr set_of_chars(Iter begin, Iter end)
constexpr set_of_chars(std::initializer_list<char> set) [C++11]
Creates a set filled with characters from the specified range of values.
Note: constexpr since C++14 only!
constexpr set_of_chars(const char *c_str)
Creates a set filled with characters from the specified C-string excluding NULL-terminator.
Note: constexpr since C++14 only!
bool contains(char ch) const
Checks whether the set contains ch.
constexpr void add(char ch)
Adds ch to the set.
Postcondition: contains(ch) == trueNote: constexpr since C++14 only!
constexpr void remove(char ch)
Removes ch from the set.
Postcondition: contains(ch) == falseNote: constexpr since C++14 only!
template<class Iter> constexpr void add(Iter begin, Iter end)
constexpr void add(std::initializer_list<char> set) [C++11]
Calls add(ch) for each value in the range.
Note: constexpr since C++14 only!
constexpr void add(const char *c_str)
Calls add(ch) for each character in the C-string excluding NULL-terminator.
Note: constexpr since C++14 only!
template<class Iter> void assign(Iter begin, Iter end)
void assign(const char *c_str)
void assign(std::initializer_list<char> set) [C++11]
Calls clear() then add() with the specified parameters.
void clear()
Removes all elements from the set.
Postcondition: contains(char(ch)) == false for any char
ISO C99 <stdint.h> for C++98. Since C++11 - just a redirector to <cstdint>.
Additionally, some metafunctions for template metaprogramming are provided.
Following types are guaranteed to be available in the global namespace:
Following types are guaranteed to be available in the global namespace:
Following types are guaranteed to be available in the global namespace:
Following types are guaranteed to be available in the global namespace:
Following types are guaranteed to be available in the global namespace:
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
Metafunctions returning the signed/unsigned interger type of the requested exact size in bytes. Shorter aliases are available in C++11 mode and higher. Valid SizeInBytes values are 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) );
std::FILE-related C++ wrappers.
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; };
Thin RAII-wrapper for std::FILE *. Controls file's life time. Automatic conversion to std::FILE * allows usage of the object whereever the file pointer is allowed/required.
explicit stdio_file(std::FILE *fp = nullptr)
Wraps already existing stream pointer.
Precondition: fp is either a pointer to the open file or nullptr.
stdio_file(const char *name, const char *mode)
Calls open(name, mode). The result should be checked using subsequent is_open() call!
~stdio_file()
Calls std::fclose() if is_open() == true.
stdio_file(stdio_file &&o) noexcept [C++11]
stdio_file &operator=(stdio_file &&o) noexcept [C++11]
Move operations for C++11 mode.
bool open(const char *name, const char *mode)
Calls std::fopen(name, mode). Returns true in case of success.
Precondition: is_open() == false
bool is_open() const
Returns true if file is open.
void close()
Calls std::fclose(). No preliminary checks whether the file is open are performed! Throws if std::fclose() fails.
Precondition: is_open() == truePostcondition: is_open() == false
bool close_nt() noexcept
A counterpart of close() but never throws, returns false instead in case of error.
void swap(stdio_file &o) noexcept
Swaps the value with o.
std::FILE *detach_handle() noexcept
Releases the file out of the object's control.
Postcondition: is_open() == false
std::FILE *attach_handle(std::FILE *fp) noexcept
Takes fp under control and returns the previous handle value.
Precondition: fp is either a poiner to the open file or nullptr.Postcondition: handle() == fp
std::FILE *handle() const
Returns the held handle value.
operator std::FILE*() const
Allows usage of the object as 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() also will be called in case of exception by destructor
sread_result<char> read(std::FILE *fp);
Attempts to read a byte from the C-stream. Returns the succesful result on success, unsuccessful result on EOF or throws on error.
void write(std::FILE *fp, char ch);
Writes a byte to the C-stream. Throws on error.
bool getline(std::FILE *fp, std::string &str, char delim = '\n');
A counterpart of std::getline but for C-stream. Returns false if the end of the file was reached and nothing was read.
#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
The set of functions converts a string, that represents an integer in decimal notation, to one of the standard C++ integer types. The string can be a C-string as well as std::string.
As opposed to the standard converters, like std::strtol(), strict validation of the format of the whole string and value range is performed. In particular leading spaces and non-digit characters at the end are not allowed. For unsigned types minus ('-') is disallowed, which is happily accepted by std::strtoul() by unknown reason.
In case of errors the following exceptions are thrown:
There are two kinds of the function prototypes:
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);
The functions are complete analogs of decimal_to_number() except they work with generic ranges of characters instead of strings.
#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);
A counterpart of decimal_to_number() functions but doesn't throw exceptions. The returned object is boolean-testable and indicates the success of the operation. Also it contains the resulting number in case of success, or an error code otherwise. See number_parse_result for more information.
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; // for brevity only 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; };
A counterpart of decimal_to_number() functions but doesn't throw exceptions, number_parse_status codes are returned instead by parse().
number_parse_status::ok
Success, the result can be obtained using result() function.
number_parse_status::invalid_number
The string is not a correct decimal integer.
number_parse_status::unrepresentable
The string is probably correct but the result cannot be represented by the used type (integer overflow).
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]
Converts a range of characters or a string to a number.
Postcondition: The result of conversion can be obtained by result() call if number_parse_status::ok is returned.
[[nodiscard]] T result() const
Returns the result of conversion produced by the the last parse() call.
Precondition: The last parse() call returned 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' contains the result of conversion }
enum class number_parse_status : unsigned char { ok, invalid_number, unrepresentable }; using number_parse_status_t = number_parse_status; // for C++98
Number parsing outcome status codes.
typename number_parse_status_t
Use this as a name of type if your code has to be C++98-compatible.
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 + the resulting value in case of success.
number_parse_result(number_parse_status_t s)
Precondition: s != number_parse_status::okPostcondition: status() == s
explicit number_parse_result(T n)
Postcondition: has_value() && value() == n
number_parse_status_t status() const
A status of the number parsing operation.
bool has_value() const
explicit operator bool() const
Returns status() == number_parse_status::ok.
T value() const
The resulting number.
Precondition: 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;
Class is an extended and improved std::string. It has following advantages:
str << "Error message: " << err_msg << "\n";
__vic::string_buffer st(4096); // is same as 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");)
In spite of all these improvements, the interface of this class is completely backward compatible with std::string. Objects can be passed in contexts that require std::string. Class has no additional data-members.
Using of inserter (operator <<) provides the easiest way to convert numbers to decimal string representation. Using of std::ostringstream for this purposes is more functional (you can specify radix, formatting, etc) but too tedious and not efficient in most cases.
Additionally this type has a short synonym msg. It is very convenient for usage when one needs to construct a complex message with a single expression, without introducing unnecessary variables:
oresult res = db_open(db_name); if(res != 0) throw __vic::exception( __vic::msg(64) << "Cannot open DB " << db_name << ". res = " << res );
As you can see, the maximum size of the message expected is specified in the constructor for optimization purposes.
string_buffer()
Create an empty string.
Postcondition: empty() == true
explicit string_buffer(size_type n)
Calls reserve(n).
Postcondition: capacity() >= n
string_buffer(const char *str)
string_buffer(std::string str)
Creates the copy of str.
string_buffer(const std::string &str, size_type off, size_type n = npos)
Creates the copy of str substring.
string_buffer(const char *char_buf, size_type n)
Creates string using buffer and its length.
string_buffer(string_ref sr)
string_buffer(const char *begin, const char *end)
template<class InputIterator> string_buffer(InputIterator begin, InputIterator end)
Creates string from the chars range.
template<class T> string_buffer &operator<<(const T &v)
Calls to_text_append(v, *this).
string_buffer &reserve(size_type n)
string_buffer &clear()
Calls the corresponding std::string operation and additionally returns the reference to itself, so the call can be used in complex expressions.
reference front()
reference back()
const_reference front() const
const_reference back() const
void pop_back()
Missed container operations in the std::string interface in C++98.
operator const char *() const
Calls 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)
Concatenation of strings and characters.
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
The class represents a reference to the read-only continuous range of characters. When used as a return value, it is significantly more lightweight than std::string, because no string copy or memory allocation performed. But, when std::string is required, automatic conversion happens. Let's consider the example:
class C { std::string v; public: std::string get_v_1() const { return v; } __vic::string_ref get_v_2() const { return v; } };
As you can see, class contains one string field. Two read-only access-functions are defined. The first as usual returns std::string, the second - string_ref. When the first is used, temporay string is created every time. When the second is used, just reference is returned.
Another use case - input read-only string argument. The class is a drop-in replacement for const std::string &. In most cases, it can also be used instead of const char *. The overhead in this case is an additional scan of the string to find the NULL-terminator, which is nothing in cases when we need the string end or length anyway. Let's consider 3 sets of overloaded functions:
void f1(const std::string & ); void f2(const std::string & ); void f2(const char * ); void f3(string_ref );
Each of them can be used as
fx("Nul-terminated string");
as well as
fx(std::string("std::string"));
But with f1() we will have redundant string copying in the first case, just to read the value. With f2() several overloads are required. And while it isn't a big issue when function has single argument, with two or more string arguments it quickly becomes very tedious. The last alternative - f3() - is at the same time as short and universal as f1() and "friendlier" to the string literals and strings from the C-world - they are not copied to the heap and not converted to std::string.
basic_string_ref()
Postcondition: empty() == true
basic_string_ref(const charT *str)
template<class Traits, class Alloc> basic_string_ref(const std::basic_string<charT,Traits,Alloc> &str)
Creates reference to str.
Postcondition: *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]
Create reference to the range of the characters.
template<class Traits>
basic_string_ref(std::basic_string_view<charT,Traits> s) [C++17]
operator std::basic_string_view<charT>() const [C++17]
Converters from/to std::basic_string_view.
iterator begin() const
iterator cbegin() const
const charT *data() const
Begin of the range of the characters.
iterator end() const
iterator cend() const
End of the range of the characters.
charT front() const
charT back() const
The first and the last character of the string correspondingly.
Precondition: !empty()
charT operator[](size_t i) const
i-th character of the string.
Precondition: i < length()
bool empty() const
Returns begin() == end().
size_t size() const
size_t length() const
Size of the string.
int compare(basic_string_ref s) const
Compares the string with s. Returning values are the same as for 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
Explicit converter to std::basic_string.
operator std::basic_string<charT>() const
Implicit converter to 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)
Full set of the comparison operators.
template<class charT, class Traits> std::basic_ostream<charT,Traits> &operator<<( std::basic_ostream<charT,Traits> &os, const basic_string_ref<charT> &sr)
Inserter to an output stream. Defined (and <ostream> is included) only if __VIC_DEFINE_OSTREAM_INSERTERS macro was defined before the header inclusion.
C c; // see the class description above __vic::string_ref s = c. get_v_2(); // print the string using different ways 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; // automatic conversion to std::string std::string ss = s;
Miscellaneous strings-related utilities.
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); #if __cpp_lib_string_view // C++17 std::string_view trimmed(std::string_view str); std::string_view trimmed_front(std::string_view str); std::string_view trimmed_back(std::string_view str); std::string_view trimmed(std::string_view str, char ch); std::string_view trimmed_front(std::string_view str, char ch); std::string_view trimmed_back(std::string_view str, char ch); std::string_view trimmed(std::string_view str, const char *set); std::string_view trimmed_front(std::string_view str, const char *set); std::string_view trimmed_back(std::string_view str, const char *set); #else // until C++17 std::string trimmed(const std::string &str); std::string trimmed_left(const std::string &str); std::string trimmed_right(const std::string &str); std::string trimmed(const std::string &str, char ch); std::string trimmed_left(const std::string &str, char ch); std::string trimmed_right(const std::string &str, char ch); std::string trimmed(const std::string &str, const char *set); std::string trimmed_left(const std::string &str, const char *set); std::string trimmed_right(const std::string &str, const char *set); #endif
The set of functions stripping unwanted characters from the string edges. Characters to strip can be specified. One can specify single character ch as well as the set of characters set. If no characters is specified, all ASCII-whitespaces are implied. Following naming rules for the functions are used:
Functions trim modify the string in-situ and return the pointer or reference to it. If the original value should be preserved, trimmed functions should be used.
The implementation is optimized for common case when the string does not have anything to trim. In such cases no modifications of the argument are performed, the function returns immediately after the checks are completed, and the call is maximally cheap.
All nullptr values are treated as an empty string.
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);
Removes all characters from the set from the string. All nullptr values are treated as an empty string.
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);
Removes all characters satify the predicate pred. All nullptr values are treated as an empty string.
std::string &pad_front(std::string &str, size_t size, char pad_ch = ' '); char *pad_front(char *str, size_t size, char pad_ch = ' ');
Complements the string up to length size by adding the required number of pad_ch chars to the beginning. Nothing happens if str length is equal or greather than size or the pointer is null. Returns 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 = ' ');
Complements the string up to length size by adding the required number of pad_ch chars to the end. Nothing happens if str length is equal or greather than size or the pointer is null. Returns 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);
Returns true if string s starts with the specified prefix.
#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);
Returns true if string s ends with the specified suffix.
Generic functions to manipulate C-strings regardless of the underlying char-type, like std::char_traits<>. All the functions are located within __vic::tchar namespace.
Most of the functions are just generic redirectors to calls like strcpy, wcscpy, et al. Searching functions have more intelligible names that ones in the C-library and unified parameters: they always take pointers and never indices. Also the set of the functions is complemented with "logically symmetrical" ones missed in the standard library. Searching functions return nullptr in case of failure.
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);
String length in elements. Generic strlen / wcslen.
template<class charT> bool tchar::empty(const charT *str);
Checks if str is nullptr or has no characters.
namespace tchar { template<class charT> const charT *end(const charT *str); template<class charT> charT *end(charT *str); }
Pointer to the NULL-terminator. Generic strchr(str, '\0') / wcschr(str, L'\0').
template<class charT> int tchar::compare(const charT *str1, const charT *str2);
Compare two strings. Generic strcmp / wcscmp.
template<class charT> bool tchar::equal(const charT *str1, const charT *str2);
Checks if two strings are equal.
template<class charT> charT *tchar::copy(charT *dest, const charT *src);
Copy string. Generic strcpy / wcscpy.
template<class charT> charT *tchar::move(charT *dest, const charT *src);
Move the string in memory (memmove).
template<class charT> charT *tchar::concat(charT *dest, const charT *src);
Concatenate two strings. Generic 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); }
Find the first occurrence of the character or substring. Generic 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); }
Find the last occurrence of the character. Generic 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); }
Find the first occurrence of the character that satisfies the specified predicate.
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); }
Find the first occurrence of the character that doesn't satisfy the specified predicate.
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); }
Find the last occurrence of the character that satisfies the specified predicate.
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); }
Find the last occurrence of the character that doesn't satisfy the specified predicate.
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); }
Find the first occurrence of the character from the specified set. Generic 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); }
Find the first occurrence of the character absent in the specified set. Generic 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); }
Find the last occurrence of the character from the specified 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); }
Find the last occurrence of the character absent in the specified set.
namespace tchar { template<class charT> const charT *skip(const charT *str, charT ch); template<class charT> charT *skip(charT *str, charT ch); }
Skip all occurrences of the specified character and return the pointer. Pointer to NULL-terminator is returned if no other characters is presented.
Threads support.
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;
};
Abstract base class for thread objects. Implements "Active object" pattern. Inherit it and define worker() function which content will be executed in the new thread after start() call. Then at some point in your program you must call join() to free OS resources associated with the spawned thread.
thread()
Postcondition: joinable() == false
~thread()
Calls std::terminate() if precondition is not satisfied.
Precondition: joinable() == false || alive() == false
thread(thread &&o) noexcept [C++11]
Move constructor for C++11 mode.
thread &operator=(thread &&o) noexcept [C++11]
Move assignment for C++11 mode. Calls std::terminate() if precondition is not satisfied.
Precondition: joinable() == false || alive() == false
void start()
Spawns new thread and calls worker() there.
Precondition: joinable() == falsePostcondition: joinable() == true
void cancel()
Cancels the thread execution.
Precondition: joinable() == truePostcondition: joinable() == true
void join()
Waits for the thread termination if running and makes joinable() == false.
Precondition: joinable() == truePostcondition: joinable() == false
bool alive() const
Returns true if the thread haven't been terminated yet (worker() function haven't returned).
Precondition: joinable() == true
bool joinable() const
Returns true if the object has a corresponding OS object (thread) created by the start() call and not yet destroyed by join() call.
id get_id() const
Returns ID of the thread.
native_handle_type handle() const
Returns a native OS-specific handle of the thread.
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);
Unique thread ID. It can hold a value associated with some thread or the special distinct value not associated with any thread.
id()
Creates the special value not associated with any thread.
Postcondition: bool(*this) == false
explicit operator bool() const
Returns true if the object holds ID of some thread.
native_handle_type handle() const
Returns a native OS-specific handle of the thread.
Precondition: bool(*this) == true
bool operator==(thread::id a, thread::id b)
bool operator!=(thread::id a, thread::id b)
Check if a and b represent the same thread.
Invariant: id() == id()
namespace this_thread { thread::id get_id(); void sleep_ms(unsigned msec); }
Set of the functions to control the current (calling) thread.
thread::id get_id()
Returns ID of the calling thread.
void sleep_ms(unsigned msec)
Suspends the execution of the calling thread until the time-out interval specified in milliseconds elapses.
[[noreturn]] void throw_errno(const char *prompt); [[noreturn]] void throw_errno(const char *prompt, int err_no);
Throw an exception with global errno value or given err_no correspondingly. Default exception type is libc_error at the moment but can be changed at link time by overriding this functions. For example std::system_error can be used. Just create cpp-file with the following content in your project:
#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); } //----------------------------------------------------------------------------
It's enough to override only one functions because the second one just calls throw_errno(prompt, errno).
ssize_t written = ::write(fd, buf, buf_size); if(written < 0) __vic::throw_errno("write"); // ...
Generic converters to some text representation.
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
Converts the specified first argument to some text representation and appends it to the second argument.
This function is a customization point (like std::swap) for the library. Users can define additional overloads for own types (in the same namespace where the type is defined) in order to "teach" the library how to create a text from the value of the specified type.
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)
Converts a number to decimal representation.
void to_text_append(bool f, std::string &str)
Converts a boolean value to 0 or 1.
void to_text_append(const void *p, std::string &str)
Converts a pointer to some platform-specific representation.
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]
Appends the specified string or character to the output string.
Note: nullptr value in the const char * overload is threated as an empty string.
int n = 5; std::string st = "n = "; __vic::to_text_append(n, st); assert(st == "n = 5");
Template metaprogramming support.
All the predicate metafunctions have boolean member value and, usually, derived from integral_constant.
All the type transformer metafunctions have type member type containing the conversion result.
All of the template aliases are available only in C++11 mode.
template<class T, T Val> struct integral_constant { using value_type = T; using type = integral_constant<T, Val>; static constexpr T value = Val; };
The topmost base class of the most metafunctions.
using true_type = integral_constant<bool, true>;
Base class for predicate metafunctions that have value true.
using false_type = integral_constant<bool, false>;
Base class for predicate metafunctions that have value false.
template<class T1, class T2> struct is_same;
A predicate. True if T1 and T2 are exactly the same type.
template<class T> struct is_const;
A predicate. True if T has const qualifier.
template<class T> struct is_byte;
A predicate. True, if T is a type representing byte:
template<class To, class From> constexpr To byte_cast(From v);
Casts value of From type to To type if both types are byte types (see is_byte).
template<class T> struct is_signed_integer;
A predicate. True if T is a one of the "standard signed integer types" (see the Standard).
template<class T> struct is_unsigned_integer;
A predicate. True if T is a one of the "standard unsigned integer types" (see the Standard).
template<class... B> struct conjunction;
A predicate. The logical conjunction of the predicates B.... False iff one of the B... is false.
template<class... B> struct disjunction;
A predicate. The logical disjunction of the predicates B.... True iff one of the B... is true.
template<class B> struct negation;
A predicate. The logical negation of the predicate B.
template<class T> struct remove_const; template<class T> using remove_const_t = typename remove_const<T>::type;
A type transformer. Removes the top-level const qualifier or just returns T if it doesn't have such qualifier.
template<class T> struct remove_volatile; template<class T> using remove_volatile_t = typename remove_volatile<T>::type;
A type transformer. Removes the top-level volatile qualifier or just returns T if it doesn't have such qualifier.
template<class T> struct remove_cv; template<class T> using remove_cv_t = typename remove_cv<T>::type;
A type transformer. Removes any top-level cv-qualifier or just returns T if it doesn't have such qualifiers.
template<class T> struct remove_reference; template<class T> using remove_reference_t = typename remove_reference<T>::type;
A type transformer. Returns the type referred by T or just T if it isn't a reference type.
template<class T> struct remove_cvref; template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
A type transformer. Applies remove_reference then remove_cv to T.
template<class T> struct remove_pointer; template<class T> using remove_pointer_t = typename remove_pointer<T>::type;
A type transformer. Returns the type pointed by T or just T if it isn't a pointer type.
template<bool Cond, class Then, class Else> struct conditional; template<bool Cond, class Then, class Else> using conditional_t = typename conditional<Cond, Then, Else>;
A type transformer. Returns Then if Cond == true. Else is returned otherwise.
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> {};
Classical tools for SFINAE-magic.
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>;
Implementation of C++14 std::index_sequence for C++11.
Unicode support utilities.
using unicode_t = char32_t; // since C++11 // or using unicode_t = uint_least32_t; // C++98
Type able to store any Unicode code point.
template<class UTFReader, class UTFWriter> void utf_transcode(UTFReader r, UTFWriter w);
An algorithm that reads all code points of type unicode_t from UTFReader using r() and writes them to UTFWriter using w().
constexpr unicode_t unicode_max = 0x10FFFF; constexpr unicode_t unicode_bom = 0xFEFF; constexpr unicode_t unicode_replacement_char = 0xFFFD;
Named constants for some useful 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
Values returned by utf8::reader parse() function.
constexpr bool utf8::is_error(utf8::status s);
Returns false for utf8::status::ok and utf8::status::eof values. true is returned otherwise.
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
Exception classes thrown by utf8::reader read() function. All exceptions are derived from abstract base class utf8::bad_encoding. See utf8::status for equivalent status codes.
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; };
A read result returned by utf8::reader::parse() function.
read_result(utf8::status_t s)
Precondition: s != utf8::status::okPostcondition: status() == s
unicode_t value() const
Returns the value read.
Precondition: status() == utf8::status::ok
utf8::status_t status() const
Returns the read operation status.
explicit operator bool() const
The same as status() == utf8::status::ok.
sread_result<unicode_t> utf8::convert_or_throw(utf8::read_result r);
Converts utf8::read_result to sread_result. Throws exception from __vic/utf8/exceptions.h if 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 from byte sequence reader. The sequence is accessed using ByteSReader which models sreader<unsigned char> (see S-readers).
ByteSReader &get_byte_reader()
const ByteSReader &get_byte_reader() const
Returns reference to the used byte reader.
template<class... Args>
explicit reader(Args&&... args) [C++11]
Forwards all parameters to the used byte reader.
reader() [C++98 only]
explicit reader(ByteSReader r) [C++98 only]
Constructors for C++98 mode.
utf8::read_result parse()
Tries to extract the next code point from the byte sequence using ByteSReader. Returns the code point read on success. utf8::status::eof is returned when no more bytes are available. Other statuses are returned on errors, see utf8::status for more details. ByteSReader::operator() is used to access individual bytes.
Note: The function itself doesn't throw exceptions but exception can be thrown by ByteSReader::operator().
sread_result<unicode_t> read()
sread_result<unicode_t> operator()()
The same as parse() but returns sread_result on success or on EOF. Throws an exception from __vic/utf8/exceptions.h otherwise.
template<class ByteSReader> utf8::reader<ByteSReader> utf8::make_reader(ByteSReader r)
Creates UTF-8 reader using the specified 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 to byte sequence writer. ByteSWriter which models swriter<unsigned char> (see S-writers) is used as a byte output.
ByteSWriter &get_byte_writer()
const ByteSWriter &get_byte_writer() const
Returns reference to the used byte writer.
template<class... Args>
explicit writer(Args&&... args) [C++11]
Forwards all parameters to the used byte writer.
writer() [C++98 only]
explicit writer(ByteSWriter r) [C++98 only]
Constructors for C++98 mode.
void write(unicode_t cp)
void operator()(unicode_t cp)
Writes the specified code point according to UTF-8 encoding rules. ByteSWriter::operator() is used to write individual bytes.
template<class ByteSWriter> utf8::writer<ByteSWriter> utf8::make_writer(ByteSWriter w)
Creates UTF-8 writer using specified 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 // or using code_unit_t = uint_least16_t; // C++98 } // namespace
UTF-16 code unit type.
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
Values returned by utf16::reader parse() function.
constexpr bool utf16::is_error(utf16::status s);
Returns false for utf16::status::ok and utf16::status::eof values. true is returned otherwise.
namespace utf16 { class bad_encoding; // public std::exception class truncated_code_unit; class truncated_code_point; class invalid_sequence; } // namespace
Exception classes thrown by utf16::reader read() function. All exceptions are derived from abstract base class utf16::bad_encoding. See utf16::status for equivalent status codes.
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; };
A read result returned by utf16::reader::parse() function.
read_result(utf16::status_t s)
Precondition: s != utf16::status::okPostcondition: status() == s
unicode_t value() const
Returns the value read.
Precondition: status() == utf16::status::ok
utf16::status_t status() const
Returns the read operation status.
explicit operator bool() const
The same as 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; };
The same as utf16::read_result but for utf16::code_unit_t.
sread_result<unicode_t> utf16::convert_or_throw(utf16::read_result r);
Converts utf16::read_result to sread_result. Throws exception from __vic/utf16/exceptions.h if 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 from 2-byte utf16::code_unit_t sequence reader. The sequence is accessed using the special reader of the following structure:
class CodeUnitReader
{
public:
utf16::read_unit_result operator()();
};
utf16::read_unit_result operator()()
Tries to read the next code unit. Returns the code unit read on success, utf16::status::eof if no more code units are available or utf16::status::truncated_code_unit if only partial code unit is available.
CodeUnitReader &get_code_unit_reader()
const CodeUnitReader &get_code_unit_reader() const
Returns reference to the used code unit reader.
template<class... Args>
explicit reader(Args&&... args) [C++11]
Forwards all parameters to the used code unit reader.
reader() [C++98 only]
explicit reader(CodeUnitReader r) [C++98 only]
Constructors for C++98 mode.
utf16::read_result parse()
Tries to extract the next code point from code unit sequence using CodeUnitReader. On success the code point read is returned. utf16::status::eof is returned when no more code units available. Other values are returned on errors, see utf16::status for more details. CodeUnitReader::operator() is used to access code units.
Note: The function itself doesn't throw exceptions but exception can be thrown by CodeUnitReader::operator().
sread_result<unicode_t> read()
sread_result<unicode_t> operator()()
The same as parse() but returns sread_result on success or on EOF. Throws an exception from __vic/utf16/exceptions.h otherwise.
template<class CodeUnitReader> utf16::reader<CodeUnitReader> utf16::make_reader(CodeUnitReader r)
Creates UTF-16 reader using the specified 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 to 2-byte utf16::code_unit_t sequence writer. CodeUnitSWriter which models swriter<utf16::code_unit_t> (see S-writers) is used as a code units output.
CodeUnitSWriter &get_code_unit_writer()
const CodeUnitSWriter &get_code_unit_writer() const
Returns reference to the used code unit writer.
template<class... Args>
explicit writer(Args&&... args) [C++11]
Forwards all parameters to the used code unit writer.
writer() [C++98 only]
explicit writer(CodeUnitSWriter r) [C++98 only]
Constructors for C++98 mode.
void write(unicode_t cp)
void operator()(unicode_t cp)
Writes the specified code point according to UTF-16 encoding rules. CodeUnitSWriter::operator() is used to write individual code units.
template<class CodeUnitSWriter> utf16::writer<CodeUnitSWriter> utf16::make_writer(CodeUnitSWriter w)
Creates UTF-16 writer using specified 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 };
A synchronization object like Event Object in Windows. Can be in ether of two states: signaled or nonsignaled. A thread can effectively wait for the signaled state using one of the available wait-functions (with minimal system resources consumption).
explicit waitable_event(bool signaled = false)
Postcondition: signaled() == signaled
bool signaled() const
Returns true if the object is in signaled state.
void set()
Sets the signaled state.
Postcondition: signaled() == true
void reset()
Sets the nonsignaled state.
Postcondition: signaled() == false
void wait()
Waits for the signaled state with no timeout.
Postcondition: signaled() == true
bool wait_ms(unsigned msec)
Waits for the signaled state no longer than the specified amount of milliseconds. Returns signaled().
Note: wait_for() should be used in C++11 mode instead.
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]
Waits for the signaled state no longer than the specified timeout. Returns 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++ wrapper for Win32 API HBITMAP.
Bitmap() = default
Creates an uninitialized value.
explicit Bitmap(HBITMAP h)
Postcondition: Handle() == h
static Bitmap CreateCompatible(HDC hdc, int w, int h)
Calls ::CreateCompatibleBitmap().
bool DeleteNT() noexcept
Calls ::DeleteObject() and returns false on error.
void Delete()
Calls ::DeleteObject() and throws on error.
void ClearHandle()
Postcondition: !Handle()
HBITMAP Handle() const
operator HBITMAP() const
Returns the wrapped HBITMAP value.
void Handle(HBITMAP h)
Postcondition: 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; };
A C++ wrapper for Win32 API CRITICAL_SECTION.
CriticalSection()
Calls ::InitializeCriticalSection().
explicit CriticalSection(DWORD dwSpinCount) [_WIN32_WINNT >= 0x0403]
Calls ::InitializeCriticalSectionAndSpinCount().
~CriticalSection()
Calls ::LeaveCriticalSection().
void Enter()
Calls ::EnterCriticalSection().
bool TryEnter()
Calls ::TryEnterCriticalSection() and returs the result.
void Leave() noexcept
Calls ::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(); };
A scoped lock guard. Works with ::CRITICAL_SECTION as well as with windows::CriticalSection objects.
explicit CSGuard(::CRITICAL_SECTION &cs)
explicit CSGuard(CriticalSection &cs)
Calls ::EnterCriticalSection(&cs).
CSGuard(::CRITICAL_SECTION &cs, adopt_t)
CSGuard(CriticalSection &cs, adopt_t)
Only saves the reference to cs without ::EnterCriticalSection() call.
~CSGuard()
Calls ::LeaveCriticalSection(&cs).
// Typical usage void reentrant_function() { static __vic::windows::CriticalSection cs; __vic::windows::CSGuard guard(cs); // Critical section code until the end of the block ... } // Usage of the adopt-constructor __vic::windows::CriticalSection cs; if(cs.TryEnter()) // Try to enter the critical section { // Success using __vic::windows::CSGuard; CSGuard guard(cs, CSGuard::adopt); // Critical section code until the end of the block ... } else { // The critical section is busy ... }
C++ wrappers for 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; };
General purpose HDC wrapper. Use windows::ClientDC instead of ::ReleaseDC(). Use windows::PaintDC instead of ::BeginPaint()/::EndPaint().
DC() = default
Creates an uninitialized value.
explicit DC(HDC hdc)
Postcondition: Handle() == hdc
static DC CreateCompatible(HDC hdc)
Calls ::CreateCompatibleDC().
bool ReleaseNT(HWND hwnd) noexcept
Calls ::ReleaseDC() and returns false on error.
void Release(HWND hwnd)
Calls ::ReleaseDC() and throws on error.
bool DeleteNT() noexcept
Calls ::DeleteDC() and returns false on error.
void Delete()
Calls ::DeleteDC() and throws on error.
int GetHorzRes() const
Returns a horizontal resolution of the DC.
int GetVertRes() const
Returns a vertical resolution of the DC.
HWND GetWindow() const
Calls ::WindowFromDC() and returns its result.
HGDIOBJ GetCurrentObject(UINT uObjType) const
Calls ::GetCurrentObject() with the specified uObjType and returns its result.
HBRUSH GetCurrentBrush() const
HPEN GetCurrentPen() const
HFONT GetCurrentFont() const
HBITMAP GetCurrentBitmap() const
Calls ::GetCurrentObject() for the corresponding object type and returns its result.
void Select(HGDIOBJ hObj)
Calls ::SelectObject() for object that is not a region and throws on error.
void Select(HRGN hReg)
Calls ::SelectObject() for region object and throws on error.
COLORREF SetPixel(int x, int y, COLORREF c)
Calls ::SetPixel() and returns its result.
bool MoveTo(int x, int y, POINT *p = 0)
Calls ::MoveToEx() and returns false on error.
bool LineTo(int x, int y)
Calls ::LineTo() and returns false on error.
bool PolyBezier(const POINT *lppt, DWORD cPoints)
Calls ::PolyBezier() and returns false on error.
bool FillRect(const RECT &rect, HBRUSH hbr)
Calls ::PolyBezier() and returns false on error.
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
Calls ::BitBlt() and returns false on error.
Note: ::GetLastError() can be used to get extended error information.
void ClearHandle()
Postcondition: !Handle()
HDC Handle() const
operator HDC() const
Returns the wrapped HDC value.
void Handle(HDC hdc)
Postcondition: Handle() == hdc
class windows::ClientDC : public windows::DC, private non_copyable { public: explicit ClientDC(HWND hwnd); ClientDC(HDC hdc, HWND hwnd); ~ClientDC(); };
RAII-wrapper. Calls ::ReleaseDC() in destructor.
explicit ClientDC(HWND hwnd)
Calls ::GetDC(hwnd). Throws on error.
ClientDC(HDC hdc, HWND hwnd)
Adopts hdc for the subsequent release in the destructor.
Precondition: hdc is a valid handler returned by ::GetDC(hwnd) call.Postcondition: Handle() == hdc
~ClientDC()
Calls ::ReleaseDC().
class windows::PaintDC : public windows::DC, public PAINTSTRUCT, private non_copyable { public: explicit PaintDC(HWND hwnd); ~PaintDC(); };
Wrapper for PAINTSTRUCT and ::BeginPaint()/::EndPaint() calls.
explicit PaintDC(HWND hwnd)
Calls ::BeginPaint(hwnd, this). Throws on error.
~PaintDC()
Calls ::EndPaint().
Windows-specific error handling tools.
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; };
Wrapper for Windows system error codes returned by Win32 API function GetLastError(). See libc_error for reference.
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; };
Wrapper for Win32 API event synchronization object.
explicit Event(bool bManualReset, bool bInitialSignaled = false, LPCWSTR lpName = nullptr)
Creates the object using ::CreateEvent(). Throws on error.
~Event()
Destroys the object.
void Set()
Calls ::SetEvent(). Throws on error.
void Reset()
Calls ::ResetEvent(). Throws on error.
bool Wait(DWORD timeout = INFINITE) const
Calls ::WaitForSingleObject(). Throws on error.
bool wait_for(std::chrono::milliseconds ms) const [C++11]
chrono-friendly wrapper for Wait().
HANDLE handle() const
Returns 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; };
Wrapper for Win32 API FindFirstFile()/FindNextFile() functions. Special entries . and .. are newer included in the resulting set.
FindFile()
Postcondition: IsOpen() == false
~FindFile()
Calls CloseNT() if IsOpen() == true.
bool FindFirst(LPCTSTR filename)
Calls FindFirstFile(). Returns false if no files found. Throws windows::error on other errors.
Precondition: IsOpen() == false
bool FindNext()
Calls FindNextFile(). Returns false if no more files available. Throws windows::error on other errors.
Precondition: IsOpen() == true
bool Close()
Closes the find handle. Throws windows::error on errors.
Precondition: IsOpen() == truePostcondition: IsOpen() == false
bool CloseNT() noexcept
Same as Close() but returns false instead of throwing exceptions.
Precondition: IsOpen() == truePostcondition: 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; };
A C++ wrapper for Win32 API HANDLE.
Handle() = default
Creates an uninitialized value.
constexpr Handle(HANDLE h)
Implicit converter from HANDLE.
Postcondition: *this == h
operator HANDLE() const
Implicit converter to HANDLE.
bool IsInvalid() const
Returns true if the handle has value INVALID_HANDLE_VALUE.
void SetInvalid()
Assigns value INVALID_HANDLE_VALUE to the handle.
Postcondition: IsInvalid() == true
void Close()
static void Close(HANDLE h)
Calls ::CloseHandle(). Throws on error.
Precondition: The same as for ::CloseHandle().
bool CloseNT() noexcept
Calls ::CloseHandle() and returns true on success. On failure ::GetLastError() can be used to get the error description.
Precondition: The same as for ::CloseHandle().
bool Wait(DWORD timeout = INFINITE) const
Calls ::WaitForSingleObject() with the specified timeout. Returns false in case of WAIT_TIMEOUT. Throws on error. windows::WaitAbandoned is thrown in case of WAIT_ABANDONED.
void swap(Handle &o) noexcept
Swaps the value with o.
struct windows::WaitAbandoned : public std::exception { const char *what() const noexcept; };
An exception class thrown by __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; };
Virtual in-memory DC.
ShadowDC()
Postcondition: !IsCreated()
~ShadowDC()
Calls Destroy() is IsCreated() == true.
void Create(HWND hwnd, int w, int h)
Creates a shadow DC compatible with the specified window and having the specified dimensions. Throws on errors.
Precondition: !IsCreated()Postcondition: IsCreated() == true
void Create(HWND hwnd)
Creates a shadow DC compatible with the specified window and having the same dimensions. Throws on errors.
Precondition: !IsCreated()Postcondition: IsCreated() == true
void Destroy()
Destroys the DC.
Precondition: IsCreated() == truePostcondition: !IsCreated()
bool IsCreated() const
Returns true if the object has an associated shadow DC.
[[noreturn]] void throw_last_error(const char *prompt); [[noreturn]] void throw_last_error(const char *prompt, DWORD code);
Throw an exception with the global GetLastError() value or the given code correspondingly.
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);
Same as windows::throw_last_error(), but for the error codes returned by WSAGetLastError().
class windows::WaitCursor : private non_copyable { public: explicit WaitCursor(HCURSOR h); ~WaitCursor(); static HCURSOR GetDefault(); };
A RAII-wrapper. Temporarily replaces the mouse cursor with the one given in the constructor. The destructor restores the previous cursor.
explicit WaitCursor(HCURSOR h)
Replaces current cursor with h.
~WaitCursor()
Restores the cursor.
static HCURSOR GetDefault()
Returns the default wait-cursor handle (IDC_WAIT).
using __vic::windows::WaitCursor; static HCURSOR hWaitCursor = WaitCursor::GetDefault(); { WaitCursor _(hWaitCursor); // the cursor is replaced here by hWaitCursor some_long_processing(); // here the destructor restores the cursor }
UTF-8 <-> UTF-16 interconversion tools.
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 with automatic conversion to 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 to UTF-16 converter.
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 to UTF-8 converter.
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
Same as windows::utf16to8() but appends the result to res. Returns a reference to 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; };
Thin wrapper for Win32 API HWND.
Window() = default
Creates an uninitialized value.
explicit Window(HWND hwnd)
Postcondition: Handle() == hwnd
static void Register(WNDCLASSEXW wcl)
Calls ::RegisterClassExW(). Throws on error.
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)
Calls ::CreateWindowExW() and updates the wrapped HWND value. Throws on error.
void Create(const CREATESTRUCTW &cs)
Calls Create() with the values specified in cs. Throws on error.
bool Destroy() noexcept
Calls ::DestroyWindow(). Return true> on success.
LRESULT SendMessage(UINT msg, WPARAM w = 0, LPARAM l = 0)
Calls ::SendMessage() and returns the returned value.
void Update()
Calls ::UpdateWindow(). Throws on error.
bool Show(int nWinMode)
Calls ::ShowWindow() and returns the returned value.
void Redraw(const RECT *rect = 0, HRGN hrgn = 0, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW)
Calls ::RedrawWindow(). Throws on error.
void GetRect(RECT &r) const
RECT GetRect() const
Calls ::GetWindowRect(). Throws on error.
void GetClientRect(RECT &r) const
RECT GetClientRect() const
Calls ::GetClientRect(). Throws on error.
void SetText(LPCWSTR st)
Calls ::SetWindowTextW(). Throws on error.
void SetText(const char *st)
Converts the specified string from UTF-8 to UTF-16 then calls SetText(). Throws on error.
int GetText(LPWSTR st, int nMax) const
Calls ::GetWindowTextW() and returns the returned value.
void SetPos(HWND hWndAfter, int x, int y, int w, int h, UINT uFlags)
Calls ::SetWindowPos(). Throws on error.
void SetPos(int x, int y, int w, int h)
Sets position and size of the window. Throws on error.
void SetSize(int w, int h)
Sets size of the window. Throws on error.
void SetClientAreaSize(int w, int h)
Sets client area size of the window. Throws on error.
void MoveTo(int x, int y)
Moves the window to the specified position. Throws on error.
void MoveToCenter()
Moves the window to the center of the screen. Throws on error.
HWND Handle() const
operator HWND() const
Returns the wrapped value.
void Handle(HWND hwnd)
Postcondition: Handle() == hwnd
// Skeleton Win32 API application #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; } };
Wrapper for 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; } };
Wrapper for 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)
Calls ::MessageBoxW() and returns the returned value. hwnd parameter is NULL (HWND_DESKTOP) when not specified. char-functions convert strings from UTF-8 to UTF-16.
WPARAM windows::MessageLoop(HWND hwnd = nullptr);
Calls ::GetMessage() while it returs nonzero (until WM_QUIT is read). Then calls ::TranslateMessage() and ::DispatchMessage() with the message. Returns MSG::wParam.
bool windows::ProcessMessages(HWND hwnd = nullptr);
Like windows::MessageLoop() but returns when message queue is empty. Returns true if can be called again and false if WM_QUIT message was read.
template<class T>
interface sreader
{
sreader(sreader &&o); or sreader(const sreader &o);
sreader_result<T> auto operator()(); // throws on errors
};
sreader is a concept used by the library algorithms to read logical sequence of elements of type T element by element. It is a generalization and rework of the input iterator concept. In particular it handles streams, NULL-terminated strings and other sequences where end-iterator is meaningless or expensive to obtain better. At the same time, a traditional [begin,end) pair of iterators is just a particular case and fully and well handled by __vic::iterator_sreader adapter.
"sreader" is pronounced as "S-reader" where S stands for "Sequential", "Sequence" or "Stream".
When the particular class meets the requirements of this concept for some T it is said to model the sreader<T> concept.
Each sreader object has to be move- or copy-constructible.
sreader_result<T> auto operator()()
Attempts to get the next element of the sequence. On success the returned object is true and its value() member-functions returns the element read. If no element available (EOF), returns false. Throws an exception in the other cases.
Values returned by sreaders (sreader_result) provide the following interface:
template<class T>
interface sreader_result
{
explicit operator bool() const;
T value() const;
};
An sreader read result contains a status of the read operation (success/failure) and the value read if the read was successful. The instances are (trivially) copy-constructible and copy-assignable.
sreader result types provide structured bindings support in the following form:
auto [value, ok] = reader();
Since C++98 lacks auto type specifiers, usage of different result types is impractical. In this language mode sreaders return sread_result type (or a type implicitly convertible to it).
explicit operator bool() const
Returns true if the read was successful and value() contains the value read. false usually means EOF.
T value() const
Returns the value read.
Precondition: *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>; };
A C++ concept for sreaders.
template<class R, class T> concept byte_sreader = sreader<R, T> && is_byte<T>::value;
A C++ concept for sreaders that read bytes.
template<class R, class T> concept sreader_result = std::semiregular<R> && requires(R r) { bool{r}; {r.value()} -> std::same_as<T>; };
A C++ concept for sreader results.
template<class SReaderResult> struct sreader_value { typename type; }; template<class SReaderResult> using sreader_value_t = typename sreader_value<SReaderResult>::type;
A metafunction returning the type returned by the value() member-function of the specified SReaderResult.
__vic::sreader_value<__vic::sread_result<int> >::type value; // int value;
template<class ByteSReaderResult> unsigned char uchar_value(ByteSReaderResult r);
Returns the resulting byte value – r.value() – as an unsigned char.
This function is defined only if 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
A concrete type (template) that models sreader_result [C++20].
The sread_result<unsigned char> specialization is compatible (implicitly convertible) with any byte result type representation including:
__vic::sread_result<char> char_reader(); __vic::sread_result<unsigned char> res = char_reader(); // OK
The __VIC_SREAD_RESULT(T) macro is used in a code that is required to be C++98-compable to specify the return type instead of auto:
__VIC_SREAD_RESULT(char) result = chars_reader();
sread_result()
sread_result(sread_eof_t)
Postcondition: !*this
sread_result(T v)
Postcondition: *this && value() == v
explicit operator bool() const
Returns the read operation status.
T value() const
Returns the value read.
Precondition: *this == true
unsigned char uchar_value(sread_result<unsigned char> r)
Returns r.value().
Precondition: r == true
struct sread_eof_t; inline constexpr sread_eof_t sread_eof;
The special value used to (implicitly) construct an unsuccessfull sread_result of any value type.
#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);
An adapter for a traditional [begin,end) pair of iterators.
Additional position() function returns the current iterator position.
Can be created using constructor or one of make_... functions.
#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);
An adapter for iterator + elements counter.
Additional position() function returns the current iterator position.
Can be created using constructor or one of make_... functions.
#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_scontainer_sreader(const Cont &c); template<class T, class Cont> container_sreader<Cont,T> make_container_sreader_for(const Cont &c);
An adapter for STL-style container classes with begin() and end() members.
Additional position() function returns the current iterator position.
Can be created using constructor or one of make_... functions.
#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);
An adapter for NULL-terminated C-style strings.
Additional position() function returns the current pointer position.
Can be created using constructor or make_... function.
#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);
An adapter for std::basic_string.
Additional position() function returns the current pointer position.
Can be created using constructor or make_... function.
#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);
Models sreader<char> for std::FILE.
Can be created using constructor or make_... function.
template<class T>
class swriter
{
public:
swriter(swriter &&o); or swriter(const swriter &o);
void operator()(T v); // throws on errors
};
swriter is a concept used by the library algorithms to write logical sequence of elements of type T element by element. It is a generalization and rework of the OutputIterator concept. In particular it handles streams and other output sequences where end-iterator is meaningless or expensive to obtain better. At the same time, a traditional output iterators are just a particular case and fully and well handled by __vic::iterator_swriter adapter.
"swriter" is pronounced as "S-write" where S stands for "Sequential", "Sequence" or "Stream".
When the particular class meets the requirements of this concept for some T it is said to model the swriter<T> concept.
Each instance of the class has to be move- or copy-constructible.
void operator()(T v)
Writes the element or throws an exception on error.
template<class W, class T> concept swriter = std::movable<W> && requires(W w, const T v) { w(v); };
A C++ concept for swriters.
#include<__vic/swriters/null.h> class null_swriter { public: template<class T> void operator()(T v) {} }; null_swriter make_null_swriter();
Fake writer that accepts any value and does nothing with it (like UNIX /dev/null).
#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);
An adapter. Uses push_back() member function to write elements. Can be created using constructor or one of make_... functions.
#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);
Output iterator writer. Can be created using constructor or one of make_... functions.
#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);
An adapter for std::basic_string. Can be created using constructor or make_... function.
#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);
Models swriter<char> for std::FILE. Can be created using constructor or make_... function.
The library provides tools for parsing and reading configuration files. The tools are:
Configuration file is a text file with a set of named parameters and their values. Parameters can be:
Also, from the multiplicity point of view, they (both atomic and complex) can be:
Each parameter has a name and a value. Atomic parameters are defined like this:
name: value
Lists consist of a set of values of the same type. Two equivalent notation can be used to define them. Use any of them you find more convenient.
Notation 1:
list_name: value1 list_name: value2 list_name: value3
Notation 2:
list_name { value1 value2 value3 }
As you can see, the first notation is the same as that used for the scalar types. The only difference is that a scalar parameter gets only the last value, while a list gets all of them.
Both notations can be combined. For example, if you want the value }, it can be added using the first notation with a colon. The rest can be added using braces:
chars { $ # { } chars: }
A snippet like this creates a list of 4 elements: "$", "#", "{", "}".
Multiline values can be specified the following syntax:
name <<EOF string 1 string 2 ... EOF
Where EOF is any sequence of Latin letters. The syntax and processing logic are the same as for the Bourne Shell "here-document" construct.
Complex parameters consist of a set of subparameters. For example:
complex_param ( scalar_subparam: value list_subparam { value1 value2 } complex_subparam ( ... ) )
As mentioned above, complex parameters can also be lists. The second list notation (see above) for complex parameters looks like this:
complex_list_param { ( subparam: value1 ... ) ( subparam: value2 ... ) }
The first list notation for complex parameters is allowed as well.
Lines starting with the '#' character are treated as comments and are ignored by the parser.
If different files contain the same subset of parameters and their values, this common part can be moved to a separate file and then simply included in them. The following syntax is used:
... <common.cfg> param: value ...
common.cfg here is the name of the included file. The set of parameters from it is included in the current configuration file.
The configuration in the code is represented as a struct with a set of fields corresponding to the configuration parameters. The names do not have to be the same as in the file. On instantiation of such struct, each field must get some default value.
A complex parameter is also a struct and can be treated as a configuration. In fact, the only difference is that they do not occupy the entire file, only a fragment between the parens.
The base class config::parser [C++11] is created to fill in such structs. To create your own parser, you derive a class from the base parser and then describe the set of parameters in the constructor. There are 2 ways to describe an atomic parameter: either using the __VIC_REGISTER_CONFIG_PARAM()/__VIC_REGISTER_CONFIG_PARAM_VP() macros, or using the register_param() member function defined in config::parser. The first method is more convenient if the parameter name is the same in both the struct and the file – you do not have to specify it twice. Complex parameters are described using either the __VIC_REGISTER_COMPLEX_CONFIG_PARAM() macro, or the register_complex_param() member function.
Here is an example. Suppose your code contains a structure with configuration parameters, like this:
struct myconfig { myconfig() = default; // sets the parameters to their default values int int_param = 0; std::string str_param{"default"}; std::vector<std::string> list_param; };
Let's assume that all the parameters except str_param have the same name in the file, and str_param is called "string" in the file. Now let's define a parser for the structure:
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); } };
The final step is to create an instance of the parser and apply it to the file using the parse() function:
myconfig cfg; myconfig_parser parser(cfg); parser.parse("conf/my.cfg");
Now we have the cfg structure filled with values from the file.
For each complex parameter you have to define the corresponding config::parser and register it using register_complex_param().
The type of a configuration parameter is determined by its type in the code, and its value is validated during parsing. The library has built-in support for some types:
By including additional headers, you can also add support for:
And also lists with elements of the types mentioned above:
If you are using an unsupported type for your parameter, you can add such support yourself. All you need to do is specialize the config::value<> template for your type in the following form:
namespace __vic { namespace config { ///////////////////////////////////////////////////////////////////////////// template<> struct value<T> { static bool parse(const std::string &s, T &res) { // TODO: parse s } }; ///////////////////////////////////////////////////////////////////////////// }}
Where T is your type. The string representation of the value from the file is passed to the parse() function. It must parse it, convert to a value of the type T, and assign the value to the reference specified as the second parameter of the function. If the function fails to convert the value, it must return false.
Example for type 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 }
In general, a value parser doesn't have to be a specialization of config::value. There are situations when 2 parameters have the same type, but different representation in the file, so they need to be parsed differently. And the library provides such capabilities.
For example, we want to specify size in the configuration file in megabytes and gigabytes, for which we are going to use the suffixes M and G:
max_log_file_size: 2M
Obviously, if we just specify the type as unsigned and register the parameter in the constructor of the file parser, the config::value<unsigned> parser will be used and it will fail when a letter is encountered. The solution is to create your own value parser, like this:
struct file_size_value { static bool parse(const std::string &st, unsigned &res) { // parse the string and save the result } };
Now we need to tell the file parser to use this value parser for the parameter. It can be done like this:
register_param<file_size_value>(cfg.max_log_file_size, "max_log_file_size"); // or __VIC_REGISTER_CONFIG_PARAM_VP(cfg, max_log_file_size, file_size_value);
That's it. Now the parameter will be parsed correctly.
The template argument for the register_param<> function template can be either a class or a class template with one type-parameter. In the latter case, the configuration parameter type will be used as a template argument for the type-parameter.
If the new parameter type is a list of values, you also need to define a specialization for the config::list_traits [C++11] template. Here is an example of adding a new container for lists of values – let's allow std::set to be used.
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)); } };
Now all parameters of type std::set<T> will be automatically treated as lists.
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) };
Base class for parsers of config files and complex parameters.
class error
Base class for exceptions thrown by the parser.
class source_error
Exception thrown on invalid config file format.
class duplicate_param
Exception throw by the register_param() functions when attempting to register a parameter with the same name.
void parse(const char *file_path)
void parse(const std::string &file_path)
Parse the configuration file specified by the path.
template<class ValueParser, class T> void register_param(T &v, const char *name)
Register a parameter with the specified name and referred by v. ValueParser will be used to parse its values.
template<template<class > class ValueParser, class T> void register_param(T &v, const char *name)
Calls register_param< ValueParser<T> >(v, name).
template<class T> void register_param(T &v, const char *name)
Calls register_param< config::value<T> >(v, name).
__VIC_REGISTER_CONFIG_PARAM(cfg, param)
Calls register_param(cfg.param, #param), which prevents duplication of the parameter name (when it is the same as the struct field name).
__VIC_REGISTER_CONFIG_PARAM_VP(cfg, papam, ValueParser)
Same as __VIC_REGISTER_CONFIG_PARAM() but additionally specifies ValueParser.
template<class Parser, class T> void register_complex_param(T &v, const char *name)
Registers a complex parameter. Parser is derived from config::parser.
__VIC_REGISTER_COMPLEX_CONFIG_PARAM(cfg, param, Parser)
Same as __VIC_REGISTER_CONFIG_PARAM() but for 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>;
Value parser for values of T type, used by default (unless specified explicitly). Parses the input string s. If successful, stores the result in res and returns true. You can create specializations for your own types.
All user-defined value parsers must have the same interface and behaviour.
template<class List>
struct config::list_traits
{
using value_type = list-element-type;
static void push(List &list, value_type &&v);
};
Specializing this template for some List type makes the library treat parameters of that type as a list.
typename value_type
Type of list elements.
static void push(List &list, value_type &&v)
Adds v to the list (usually at the end).
#include<__vic/config/values/std/optional.h> template<class T> class config::value<std::optional<T>>;
#include<mfisoft/config/values/ip_addr.h> template<> class config::value<__vic::ipv4_addr>;
#include<__vic/config/values/std/optional.h> template<class T, class Parser> class config::complex_value<std::optional<T>, Parser, false>;
#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;
Parser of parameters specifying the size in bytes. Allows values like "2K", which means 2 kilobytes. The following suffixes are supported: K – kilo, M – mega, G – giga. All the suffixes are powers of 2 (1K = 1024).
To build the library you need one of supported C++ compilers and GNU Make utility version 3.82 or higher available in your command line environment (cmd).
Currently supported compilers are:
Go to src subdirectory and write:
C:\> gmake
The library file will be built for you.
C++17 is used by default but you can choose another language standard version using std option. E.g. to build the library in C++98 mode write:
C:\> gmake std=98
Available values include: 98, 11, 14, 17, 20 and 23.
Also you can choose the used compiler manually:
C:\> gmake compiler=gcc
Alternatively NMAKE utility can be used with Visual C++ and clang-cl instead of gmake:
C:\> nmake -f Makefile.nmake compiler=msvc
Finally, to use the library you just need to copy the library file and contents of include subdirectory to some place where compiler/linker can find it.