#pragma once #include #include #include #include #include #include #include /* There are two "string" classes: ByteString and String. They have nearly identical interfaces, except that one stores 8-bit octets (bytes) and the other stores Unicode codepoints. For ease of use, a String can be constructed from a string literal (char const[]) however not char const *. Exercise care when working with char arrays. Both classes inherit from std::basic_string (std::string is an instatiation of that), so all the familiar string interface is there however some helper methods have been defined: Substr(size_t start = 0, size_t count = npos) Returns a substring starting at position and counting symbols, or until end of string, whichever is earlier. If count == npos, the entire remainder of the string is included. SubstrFromEnd(size_t rstart = 0, size_t rcount = npos) Behaviourally equal to reverse(reverse(str).Substr(rstart, rcount)) but is more efficient. Useful for taking suffixes of given length or dropping a fixed number of symbols from the end. Between(size_t begin, size_t end) Returns a substring starting at and ending at . If end == npos, length of the string is used. If begin > end, an empty string is returned. Insert(size_t pos, String str) Inserts the characters from at position shifting the rest of the string to the right. Erase(size_t pos, size_t count) Starting at position erases characters to the right or until the end of string. The rest of the string is shifted left to fill the gap. EraseBetween(size_t from, size_t to) Starting at position erase until position or end of string, whichever is earlier. The rest of the string is shifted left to fill the gap. BeginsWith(String prefix) EndsWith(String suffix) Contains(String infix) Contains(value_type infix) Self-explanatory. ToLower() ToUpper() Lowercases/Uppercases characters in the string. Only works on characters in the ASCII range. ByteString::FromUtf8(bool ignoreError = true) Decodes UTF-8 byte sequences into Unicode codepoints. If ignoreError is true, then invalid byte sequences are widened as-is. Otherwise, a ConversionError is thrown. ByteString::FromAscii() Interprets byte values as Unicode codepoints. String::ToUtf8() Encodes Unicode codepoints into UTF-8 byte sequences. String::ToAscii() Narrows Unicode codepoints into bytes, possibly truncating them (!). To convert something into a string use ByteStringBuilder and StringBuilder respectively. The two use operator<< much like std::ostringstream. To convert a builder to a string use the Build() method. Alternatively you could use the static ByteString::Build and String::Build methods respectively. String::Build(x, y, z) is roughly equivalent to: StringBuilder tmp; tmp << x << y << z; return tmp.Build(); To control formatting of the input/output see "common/Format.h". If you simply want to convert a string to a number you can use the ToNumber(bool ignoreError = false) method. It can optionally take a formatting specifier as an argument: str.ToNumber(Format::Hex(), true) Otherwise to parse a string into components you can use splits. A Split is a temporary object that "remembers" how a string is divided into a "prefix", a "separator", and a "suffix". A split can also "fail", if, for example the separator was not found in the string. A Split has the following methods: Before(bool includeSeparator = false) Returns the "prefix", optionally with the "separator". After(bool includeSeparator = false) Returns the "suffix", optionally with the "separator". A Split can also be converted to bool (used in the condition of an 'if' or a 'while'), in which case it shows whether the split had succeeded or failed. The idiomatic code goes like: if(String::Split split = str.SplitBy(',')) // use split.Before() and split.After() else // str does not contain a ',' The following methods split a string: SplitBy(String sep, size_t from = 0) SplitBy(value_type sep, size_t from = 0) Split on the first occurence of the separator since position . If no such separator is found the split fails and "prefix" contains the whole string starting from . SplitByAny(String chars, size_t from = 0) Split on the first occurence of any of the characters in . SplitByNot(String chars, size_t from = 0) Split on the first occurence of any character that is *not* in . SplitFromEndBy(String sep, size_t from = npos) SplitFromEndBy(value_type sep, size_t from = npos) SplitFromEndByAny(String chars, size_t from = npos) SplitFromEndByNot(String chars, size_t from = npos) These do the same as the functions above except they try to find the *last* occurence of the separator. If the split fails it is the "suffix" that contains the whole string, and the "prefix" is empty instead. SplitNumber(number &ref, size_t pos = 0) Attempt to read a number (with the type indicated by the type of the reference) from position . In case of success store the parsed number in the reference and return a split at the end of the parse. The separator in this case is empty. If the parse fails, the "prefix" is empty. SplitNumber(number &ref, format, size_t pos = 0) Parse the number according to the provided formatting specifier. It is recommented to use ByteString::value_type and String::value_type instead of char and char32_t respectively. The reason we do not use std::wstring is that on Windows, wchar_t is a 16-bit type, which forces the usage of a UTF-16 (UCS-2) encoding to store the higher parts of the Unicode. The std::wstring implementation does not care to handle the UTF-16 encoding. Considering characters still occupy a variable amount of space (1 or 2 wchar_t's), finding the "n'th character" or "index of character at offset n" becomes a problem. (Event if that were not a problem a better solution would be to use the more space-efficient UTF-8). Therefore the String class is derived from std::basic_string, where char32_t is a type that is guaranteed to contain at least 32 bits. The drawback is that we basically lose std::stringstream (std::basic_stringstream) support and have to implement our own (See "common/String.cpp"). */ class ByteStringBuilder; class String; class StringBuilder; namespace Format { const std::ios_base::fmtflags EmptyFmtFlags = std::ios_base::dec & ~std::ios_base::dec; template struct FlagsOverride; } template class SplitBase { T const &parent; size_t posFrom; size_t posBefore; size_t posAfter; bool reverse; inline SplitBase(T const &_parent, size_t _posFrom, size_t _posBefore, size_t offset, bool _reverse): parent(_parent), posFrom(_posFrom), posBefore(_posBefore), posAfter(_posBefore == T::npos ? T::npos : _posBefore + offset), reverse(_reverse) {} public: inline T Before(bool includeSeparator = false) const { if(posBefore == T::npos) return reverse ? T() : parent.Substr(posFrom); return parent.Between(reverse ? 0 : posFrom, includeSeparator ? posAfter : posBefore); } inline T After(bool includeSeparator = false) const { if(posBefore == T::npos) return reverse ? parent.Substr(0, posFrom) : T(); return parent.Between(includeSeparator ? posBefore : posAfter, reverse ? posFrom : T::npos); } inline size_t PositionFrom() const { return posFrom; } inline size_t PositionBefore() const { return posBefore; } inline size_t PositionAfter() const { return posAfter; } inline operator bool() const { return posBefore != T::npos; } friend T; }; class ByteString : public std::basic_string { using super = std::basic_string; public: inline ByteString(): super() {} inline ByteString(value_type ch): super(1, ch) {} inline ByteString(size_type count, value_type ch): super(count, ch) {} inline ByteString(value_type const *ch, size_type count): super(ch, count) {} inline ByteString(value_type const *ch): super(ch) {} template inline ByteString(It first, It last): super(first, last) {} inline ByteString(super const &other): super(other) {} inline ByteString(super &&other): super(std::move(other)) {} inline ByteString(ByteString const &other): super(other) {} inline ByteString(ByteString &&other): super(std::move(other)) {} inline ByteString &operator=(ByteString const &other) { super::operator=(other); return *this; } inline ByteString &operator=(ByteString &&other) { super::operator=(other); return *this; } inline ByteString &operator+=(ByteString const &other) { super::operator+=(other); return *this; } inline ByteString &operator+=(ByteString &&other) { super::operator+=(std::move(other)); return *this; } inline ByteString Substr(size_t pos = 0, size_t count = npos) const { return super::substr(pos, count); } inline ByteString SubstrFromEnd(size_t rpos = 0, size_t rcount = npos) const { return super::substr(rcount == npos || rcount > rpos ? 0 : rpos - rcount, size() - rpos); } inline ByteString Between(size_t from, size_t to) const { return to == npos ? super::substr(from) : from >= to ? ByteString() : super::substr(from, to - from); } inline bool Contains(value_type ch) const { return super::find(ch) != npos; } inline bool Contains(ByteString const &other) const { return super::find(other) != npos; } inline bool BeginsWith(ByteString const &other) const { return !super::compare(0, other.size(), other); } inline bool EndsWith(ByteString const &other) const { return !super::compare(size() - other.size(), other.size(), other); } using Split = SplitBase; inline Split SplitBy(value_type ch, size_t pos = 0) const { return Split(*this, pos, super::find(ch, pos), 1, false); } inline Split SplitBy(ByteString const &str, size_t pos = 0) const { return Split(*this, pos, super::find(str, pos), str.size(), false); } inline Split SplitByAny(ByteString const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_of(str, pos), 1, false); } inline Split SplitByNot(ByteString const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_not_of(str, pos), 1, false); } inline Split SplitFromEndBy(value_type ch, size_t pos = npos) const { return Split(*this, pos, super::rfind(ch, pos), 1, true); } inline Split SplitFromEndBy(ByteString const &str, size_t pos = npos) const { return Split(*this, pos, super::rfind(str, pos), str.size(), true); } inline Split SplitFromEndByAny(ByteString const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_of(str, pos), 1, true); } inline Split SplitFromEndByNot(ByteString const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_not_of(str, pos), 1, true); } private: Split SplitSigned(long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const; Split SplitUnsigned(unsigned long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const; Split SplitFloat(double &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const; public: template inline Split SplitSigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const { long long int value = 0; Split split = SplitSigned(value, pos, set, reset); ref = value; return split; } template inline Split SplitUnsigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const { unsigned long long int value = 0; Split split = SplitUnsigned(value, pos, set, reset); ref = value; return split; } template inline Split SplitFloat(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const { double value = 0; Split split = SplitFloat(value, pos, set, reset); ref = value; return split; } inline Split SplitNumber(short int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(long long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned short int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned long long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(float &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(double &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } template inline Split SplitNumber(short int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(long long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned short int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned long long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(float &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); } template inline Split SplitNumber(double &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); } template T ToNumber(bool noThrow = false) const { T value = T(); Split split = SplitNumber(value); if(split.PositionBefore() != size()) return noThrow ? T() : throw std::runtime_error("Not a number"); return value; } template inline T ToNumber(Format::FlagsOverride fmt, bool noThrow = false) const { T value = T(); Split split = SplitNumber(value, fmt); if(split.PositionBefore() != size()) return noThrow ? T() : throw std::runtime_error("Not a number"); return value; } std::vector PartitionBy(value_type ch, bool includeEmpty = false) const; std::vector PartitionBy(ByteString const &str, bool includeEmpty = false) const; std::vector PartitionByAny(ByteString const &str, bool includeEmpty = false) const; ByteString &Substitute(ByteString const &needle, ByteString const &replacement); inline ByteString &Insert(size_t pos, ByteString const &str) { super::insert(pos, str); return *this; } inline ByteString &Erase(size_t pos, size_t count) { super::erase(pos, count); return *this; } inline ByteString &EraseBetween(size_t from, size_t to) { if(from < to) super::erase(from, to - from); return *this; } inline ByteString ToLower() const { std::locale const &loc = std::locale::classic(); ByteString value(*this); for(value_type &ch : value) ch = std::tolower(ch, loc); return value; } inline ByteString ToUpper() const { std::locale const &loc = std::locale::classic(); ByteString value(*this); for(value_type &ch : value) ch = std::toupper(ch, loc); return value; } String FromUtf8(bool ignoreError = true) const; inline String FromAscii() const; template static ByteString Build(Ts&&... args); }; inline ByteString operator+(ByteString const &lhs, ByteString const &rhs) { return static_cast const &>(lhs) + static_cast const &>(rhs); } inline ByteString operator+(ByteString const &lhs, ByteString &&rhs) { return static_cast const &>(lhs) + static_cast &&>(rhs); } inline ByteString operator+(ByteString &&lhs, ByteString const &rhs) { return static_cast &&>(lhs) + static_cast const &>(rhs); } inline ByteString operator+(ByteString &&lhs, ByteString &&rhs) { return static_cast &&>(lhs) + static_cast &&>(rhs); } inline ByteString operator+(ByteString const &lhs, std::basic_string const &rhs) { return static_cast const &>(lhs) + rhs; } inline ByteString operator+(ByteString const &lhs, std::basic_string &&rhs) { return static_cast const &>(lhs) + std::move(rhs); } inline ByteString operator+(ByteString &&lhs, std::basic_string const &rhs) { return static_cast &&>(lhs) + rhs; } inline ByteString operator+(ByteString &&lhs, std::basic_string &&rhs) { return static_cast &&>(lhs) + std::move(rhs); } inline ByteString operator+(ByteString const &lhs, ByteString::value_type rhs) { return static_cast const &>(lhs) + rhs; } inline ByteString operator+(ByteString &&lhs, ByteString::value_type rhs) { return static_cast &&>(lhs) + rhs; } inline ByteString operator+(ByteString const &lhs, ByteString::value_type const *rhs) { return static_cast const &>(lhs) + rhs; } inline ByteString operator+(ByteString &&lhs, ByteString::value_type const *rhs) { return static_cast &&>(lhs) + rhs; } inline ByteString operator+(std::basic_string const &lhs, ByteString const &rhs) { return lhs + static_cast const &>(rhs); } inline ByteString operator+(std::basic_string const &lhs, ByteString &&rhs) { return lhs + static_cast &&>(rhs); } inline ByteString operator+(std::basic_string &&lhs, ByteString const &rhs) { return std::move(lhs) + static_cast const &>(rhs); } inline ByteString operator+(std::basic_string &&lhs, ByteString &&rhs) { return std::move(lhs) + static_cast &&>(rhs); } inline ByteString operator+(ByteString::value_type lhs, ByteString const &rhs) { return lhs + static_cast const &>(rhs); } inline ByteString operator+(ByteString::value_type lhs, ByteString &&rhs) { return lhs + static_cast &&>(rhs); } inline ByteString operator+(ByteString::value_type const *lhs, ByteString const &rhs) { return lhs + static_cast const &>(rhs); } inline ByteString operator+(ByteString::value_type const *lhs, ByteString &&rhs) { return lhs + static_cast &&>(rhs); } inline bool operator==(ByteString const &lhs, ByteString const &rhs) { return static_cast const &>(lhs) == static_cast const &>(rhs); } inline bool operator==(ByteString const &lhs, std::basic_string const &rhs) { return static_cast const &>(lhs) == rhs; } inline bool operator==(ByteString const &lhs, ByteString::value_type const *rhs) { return static_cast const &>(lhs) == rhs; } inline bool operator==(std::basic_string const &lhs, ByteString const &rhs) { return lhs == static_cast const &>(rhs); } inline bool operator==(ByteString::value_type const *lhs, ByteString const &rhs) { return lhs == static_cast const &>(rhs); } inline bool operator!=(ByteString const &lhs, ByteString const &rhs) { return static_cast const &>(lhs) != static_cast const &>(rhs); } inline bool operator!=(ByteString const &lhs, std::basic_string const &rhs) { return static_cast const &>(lhs) != rhs; } inline bool operator!=(ByteString const &lhs, ByteString::value_type const *rhs) { return static_cast const &>(lhs) != rhs; } inline bool operator!=(std::basic_string const &lhs, ByteString const &rhs) { return lhs != static_cast const &>(rhs); } inline bool operator!=(ByteString::value_type const *lhs, ByteString const &rhs) { return lhs != static_cast const &>(rhs); } class String : public std::basic_string { using super = std::basic_string; public: inline String(): super() {} inline String(value_type ch): super(1, ch) {} inline String(size_type count, value_type ch): super(count, ch) {} inline String(value_type const *ch, size_type count): super(ch, count) {} inline String(value_type const *ch): super(ch) {} template inline String(It first, It last): super(first, last) {} inline String(super const &other): super(other) {} inline String(super &&other): super(std::move(other)) {} inline String(String const &other): super(other) {} inline String(String &&other): super(std::move(other)) {} template inline String(ByteString::value_type const (&ch)[N]): super(ByteString(ch, N - 1).FromAscii()) {} inline String &operator=(String const &other) { super::operator=(other); return *this; } inline String &operator=(String &&other) { super::operator=(other); return *this; } inline String &operator+=(String const &other) { super::operator+=(other); return *this; } inline String &operator+=(String &&other) { super::operator+=(std::move(other)); return *this; } inline String Substr(size_t pos = 0, size_t count = npos) const { return super::substr(pos, count); } inline String SubstrFromEnd(size_t rpos = 0, size_t rcount = npos) const { return super::substr(rcount == npos || rcount > rpos ? 0 : rpos - rcount, size() - rpos); } inline String Between(size_t from, size_t to) const { return to == npos ? super::substr(from) : from >= to ? String() : super::substr(from, to - from); } inline bool Contains(value_type ch) const { return super::find(ch) != npos; } inline bool Contains(String const &other) const { return super::find(other) != npos; } inline bool BeginsWith(String const &other) const { return !super::compare(0, other.size(), other); } inline bool EndsWith(String const &other) const { return !super::compare(size() - other.size(), other.size(), other); } using Split = SplitBase; inline Split SplitBy(value_type ch, size_t pos = 0) const { return Split(*this, pos, super::find(ch, pos), 1, false); } inline Split SplitBy(String const &str, size_t pos = 0) const { return Split(*this, pos, super::find(str, pos), str.size(), false); } inline Split SplitByAny(String const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_of(str, pos), 1, false); } inline Split SplitByNot(String const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_not_of(str, pos), 1, false); } inline Split SplitFromEndBy(value_type ch, size_t pos = npos) const { return Split(*this, pos, super::rfind(ch, pos), 1, true); } inline Split SplitFromEndBy(String const &str, size_t pos = npos) const { return Split(*this, pos, super::rfind(str, pos), str.size(), true); } inline Split SplitFromEndByAny(String const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_of(str, pos), 1, true); } inline Split SplitFromEndByNot(String const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_not_of(str, pos), 1, true); } private: Split SplitSigned(long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const; Split SplitUnsigned(unsigned long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const; Split SplitFloat(double &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const; public: template inline Split SplitSigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const { long long int value = 0; Split split = SplitSigned(value, pos, set, reset); ref = value; return split; } template inline Split SplitUnsigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const { unsigned long long int value = 0; Split split = SplitUnsigned(value, pos, set, reset); ref = value; return split; } template inline Split SplitFloat(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const { double value = 0; Split split = SplitFloat(value, pos, set, reset); ref = value; return split; } inline Split SplitNumber(short int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(long long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned short int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(unsigned long long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(float &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } inline Split SplitNumber(double &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); } template inline Split SplitNumber(short int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(long long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned short int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(unsigned long long int &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); } template inline Split SplitNumber(float &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); } template inline Split SplitNumber(double &ref, Format::FlagsOverride, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); } template T ToNumber(bool noThrow = false) const { T value = T(); Split split = SplitNumber(value); if(split.PositionBefore() != size()) return noThrow ? T() : throw std::runtime_error("Not a number"); return value; } template inline T ToNumber(Format::FlagsOverride fmt, bool noThrow = false) const { T value = T(); Split split = SplitNumber(value, fmt); if(split.PositionBefore() != size()) return noThrow ? T() : throw std::runtime_error("Not a number"); return value; } std::vector PartitionBy(value_type ch, bool includeEmpty = false) const; std::vector PartitionBy(String const &str, bool includeEmpty = false) const; std::vector PartitionByAny(String const &str, bool includeEmpty = false) const; String &Substitute(String const &needle, String const &replacement); inline String &Insert(size_t pos, String const &str) { super::insert(pos, str); return *this; } inline String &Erase(size_t pos, size_t count) { super::erase(pos, count); return *this; } inline String &EraseBetween(size_t from, size_t to) { if(from < to) super::erase(from, to - from); return *this; } inline String ToLower() const { std::locale const &loc = std::locale::classic(); String value(*this); for(value_type &ch : value) if(ch <= static_cast(std::numeric_limits::max())) ch = std::tolower(static_cast(ch), loc); return value; } inline String ToUpper() const { std::locale const &loc = std::locale::classic(); String value(*this); for(value_type &ch : value) if(ch <= static_cast(std::numeric_limits::max())) ch = std::toupper(static_cast(ch), loc); return value; } ByteString ToUtf8() const; ByteString ToAscii() const; template static String Build(Ts&&... args); }; inline String operator+(String const &lhs, String const &rhs) { return static_cast const &>(lhs) + static_cast const &>(rhs); } inline String operator+(String const &lhs, String &&rhs) { return static_cast const &>(lhs) + static_cast &&>(rhs); } inline String operator+(String &&lhs, String const &rhs) { return static_cast &&>(lhs) + static_cast const &>(rhs); } inline String operator+(String &&lhs, String &&rhs) { return static_cast &&>(lhs) + static_cast &&>(rhs); } inline String operator+(String const &lhs, std::basic_string const &rhs) { return static_cast const &>(lhs) + rhs; } inline String operator+(String const &lhs, std::basic_string &&rhs) { return static_cast const &>(lhs) + std::move(rhs); } inline String operator+(String &&lhs, std::basic_string const &rhs) { return static_cast &&>(lhs) + rhs; } inline String operator+(String &&lhs, std::basic_string &&rhs) { return static_cast &&>(lhs) + std::move(rhs); } inline String operator+(String const &lhs, String::value_type rhs) { return static_cast const &>(lhs) + rhs; } inline String operator+(String &&lhs, String::value_type rhs) { return static_cast &&>(lhs) + rhs; } inline String operator+(String const &lhs, String::value_type const *rhs) { return static_cast const &>(lhs) + rhs; } inline String operator+(String &&lhs, String::value_type const *rhs) { return static_cast &&>(lhs) + rhs; } inline String operator+(std::basic_string const &lhs, String const &rhs) { return lhs + static_cast const &>(rhs); } inline String operator+(std::basic_string const &lhs, String &&rhs) { return lhs + static_cast &&>(rhs); } inline String operator+(std::basic_string &&lhs, String const &rhs) { return std::move(lhs) + static_cast const &>(rhs); } inline String operator+(std::basic_string &&lhs, String &&rhs) { return std::move(lhs) + static_cast &&>(rhs); } inline String operator+(String::value_type lhs, String const &rhs) { return lhs + static_cast const &>(rhs); } inline String operator+(String::value_type lhs, String &&rhs) { return lhs + static_cast &&>(rhs); } inline String operator+(String::value_type const *lhs, String const &rhs) { return lhs + static_cast const &>(rhs); } inline String operator+(String::value_type const *lhs, String &&rhs) { return lhs + static_cast &&>(rhs); } template inline String operator+(String const &lhs, ByteString::value_type const (&rhs)[N]) { return static_cast const &>(lhs) + ByteString(rhs).FromAscii(); } template inline String operator+(String &&lhs, ByteString::value_type const (&rhs)[N]) { return static_cast &&>(lhs) + ByteString(rhs).FromAscii(); } template inline String operator+(ByteString::value_type const (&lhs)[N], String const &rhs) { return ByteString(lhs).FromAscii() + static_cast const &>(rhs); } template inline String operator+(ByteString::value_type const (&lhs)[N], String &&rhs) { return ByteString(lhs).FromAscii() + static_cast &&>(rhs); } inline bool operator==(String const &lhs, String const &rhs) { return static_cast const &>(lhs) == static_cast const &>(rhs); } inline bool operator==(String const &lhs, std::basic_string const &rhs) { return static_cast const &>(lhs) == rhs; } inline bool operator==(String const &lhs, String::value_type const *rhs) { return static_cast const &>(lhs) == rhs; } inline bool operator==(std::basic_string const &lhs, String const &rhs) { return lhs == static_cast const &>(rhs); } inline bool operator==(String::value_type const *lhs, String const &rhs) { return lhs == static_cast const &>(rhs); } template inline bool operator==(String const &lhs, ByteString::value_type const (&rhs)[N]) { return static_cast const &>(lhs) == ByteString(rhs).FromAscii(); } template inline bool operator==(ByteString::value_type const (&lhs)[N], String const &rhs) { return ByteString(lhs).FromAscii() == static_cast const &>(rhs); } inline bool operator!=(String const &lhs, String const &rhs) { return static_cast const &>(lhs) != static_cast const &>(rhs); } inline bool operator!=(String const &lhs, std::basic_string const &rhs) { return static_cast const &>(lhs) != rhs; } inline bool operator!=(String const &lhs, String::value_type const *rhs) { return static_cast const &>(lhs) != rhs; } inline bool operator!=(std::basic_string const &lhs, String const &rhs) { return lhs != static_cast const &>(rhs); } inline bool operator!=(String::value_type const *lhs, String const &rhs) { return lhs != static_cast const &>(rhs); } template inline bool operator!=(String const &lhs, ByteString::value_type const (&rhs)[N]) { return static_cast const &>(lhs) != ByteString(rhs).FromAscii(); } template inline bool operator!=(ByteString::value_type const (&lhs)[N], String const &rhs) { return ByteString(lhs).FromAscii() != static_cast const &>(rhs); } inline String ByteString::FromAscii() const { String destination = String(size(), String::value_type()); for(size_t i = 0; i < size(); i++) destination[i] = String::value_type(std::make_unsigned::type(operator[](i))); return destination; } inline ByteString String::ToAscii() const { ByteString destination = ByteString(size(), ByteString::value_type()); for(size_t i = 0; i < size(); i++) destination[i] = ByteString::value_type(operator[](i)); return destination; } class ConversionError : public std::runtime_error { static ByteString formatError(ByteString::value_type const *at, ByteString::value_type const *upto); public: inline ConversionError(ByteString::value_type const *at, ByteString::value_type const *upto): std::runtime_error(formatError(at, upto)) {} inline ConversionError(bool to): std::runtime_error(to ? "Could not convert to UTF-8" : "Could not convert from UTF-8") {} }; class ByteStringBuilder { std::vector buffer; public: std::ios_base::fmtflags flags; ByteString::value_type fill; size_t width, precision; inline ByteStringBuilder(): flags(std::ios_base::skipws | std::ios_base::dec), fill(' '), width(0), precision(6) {} void AddChars(ByteString::value_type const *, size_t); size_t Size() const { return buffer.size(); } ByteString Build() const; template ByteStringBuilder &operator<<(T) &&= delete; template inline ByteStringBuilder &Add(T &&arg, Ts&&... args) { return (*this << std::forward(arg)).Add(std::forward(args)...); } inline ByteStringBuilder &Add() { return *this; } }; ByteStringBuilder &operator<<(ByteStringBuilder &, short int); ByteStringBuilder &operator<<(ByteStringBuilder &, int); ByteStringBuilder &operator<<(ByteStringBuilder &, long int); ByteStringBuilder &operator<<(ByteStringBuilder &, long long int); ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned short int); ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned int); ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned long int); ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned long long int); ByteStringBuilder &operator<<(ByteStringBuilder &, ByteString::value_type); ByteStringBuilder &operator<<(ByteStringBuilder &, ByteString::value_type const *); ByteStringBuilder &operator<<(ByteStringBuilder &, ByteString const &); ByteStringBuilder &operator<<(ByteStringBuilder &, float); ByteStringBuilder &operator<<(ByteStringBuilder &, double); template ByteString ByteString::Build(Ts&&... args) { ByteStringBuilder b; b.Add(std::forward(args)...); return b.Build(); } class StringBuilder { std::vector buffer; public: std::ios_base::fmtflags flags; String::value_type fill; size_t width, precision; inline StringBuilder(): flags(std::ios_base::skipws | std::ios_base::dec), fill(' '), width(0), precision(6) {} void AddChars(String::value_type const *, size_t); size_t Size() const { return buffer.size(); } String Build() const; template StringBuilder &operator<<(T) = delete; template inline StringBuilder &Add(T &&arg, Ts&&... args) { return (*this << std::forward(arg)).Add(std::forward(args)...); } inline StringBuilder &Add() { return *this; } }; StringBuilder &operator<<(StringBuilder &, short int); StringBuilder &operator<<(StringBuilder &, int); StringBuilder &operator<<(StringBuilder &, long int); StringBuilder &operator<<(StringBuilder &, long long int); StringBuilder &operator<<(StringBuilder &, unsigned short int); StringBuilder &operator<<(StringBuilder &, unsigned int); StringBuilder &operator<<(StringBuilder &, unsigned long int); StringBuilder &operator<<(StringBuilder &, unsigned long long int); StringBuilder &operator<<(StringBuilder &, ByteString::value_type); StringBuilder &operator<<(StringBuilder &, String::value_type); StringBuilder &operator<<(StringBuilder &, String::value_type const *); StringBuilder &operator<<(StringBuilder &, String const &); StringBuilder &operator<<(StringBuilder &, float); StringBuilder &operator<<(StringBuilder &, double); template StringBuilder &operator<<(StringBuilder &b, ByteString::value_type const (&data)[N]) { return b << ByteString(data).FromUtf8(); } template String String::Build(Ts&&... args) { StringBuilder b; b.Add(std::forward(args)...); return b.Build(); } #include "common/Format.h" #include "common/Internationalization.h"