Path: blob/master/dep/rapidyaml/include/c4/format.hpp
4261 views
#ifndef _C4_FORMAT_HPP_1#define _C4_FORMAT_HPP_23/** @file format.hpp provides type-safe facilities for formatting arguments4* to string buffers */56#include "c4/charconv.hpp"7#include "c4/blob.hpp"8910#ifdef _MSC_VER11# pragma warning(push)12# if C4_MSVC_VERSION != C4_MSVC_VERSION_201713# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning)14# endif15# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe16#elif defined(__clang__)17# pragma clang diagnostic push18#elif defined(__GNUC__)19# pragma GCC diagnostic push20# pragma GCC diagnostic ignored "-Wuseless-cast"21#endif2223namespace c4 {242526//-----------------------------------------------------------------------------27//-----------------------------------------------------------------------------28//-----------------------------------------------------------------------------29// formatting truthy types as booleans3031namespace fmt {3233/** write a variable as an alphabetic boolean, ie as either true or false34* @param strict_read */35template<class T>36struct boolalpha_37{38boolalpha_(T val_, bool strict_read_=false) : val(val_ ? true : false), strict_read(strict_read_) {}39bool val;40bool strict_read;41};4243template<class T>44boolalpha_<T> boolalpha(T const& val, bool strict_read=false)45{46return boolalpha_<T>(val, strict_read);47}4849} // namespace fmt5051/** write a variable as an alphabetic boolean, ie as either true or false */52template<class T>53inline size_t to_chars(substr buf, fmt::boolalpha_<T> fmt)54{55return to_chars(buf, fmt.val ? "true" : "false");56}57585960//-----------------------------------------------------------------------------61//-----------------------------------------------------------------------------62//-----------------------------------------------------------------------------63// formatting integral types6465namespace fmt {6667/** format an integral type with a custom radix */68template<typename T>69struct integral_70{71T val;72T radix;73C4_ALWAYS_INLINE integral_(T val_, T radix_) : val(val_), radix(radix_) {}74};7576/** format an integral type with a custom radix, and pad with zeroes on the left */77template<typename T>78struct integral_padded_79{80T val;81T radix;82size_t num_digits;83C4_ALWAYS_INLINE integral_padded_(T val_, T radix_, size_t nd) : val(val_), radix(radix_), num_digits(nd) {}84};8586/** format an integral type with a custom radix */87template<class T>88C4_ALWAYS_INLINE integral_<T> integral(T val, T radix=10)89{90return integral_<T>(val, radix);91}92/** format an integral type with a custom radix */93template<class T>94C4_ALWAYS_INLINE integral_<intptr_t> integral(T const* val, T radix=10)95{96return integral_<intptr_t>(reinterpret_cast<intptr_t>(val), static_cast<intptr_t>(radix));97}98/** format an integral type with a custom radix */99template<class T>100C4_ALWAYS_INLINE integral_<intptr_t> integral(std::nullptr_t, T radix=10)101{102return integral_<intptr_t>(intptr_t(0), static_cast<intptr_t>(radix));103}104/** pad the argument with zeroes on the left, with decimal radix */105template<class T>106C4_ALWAYS_INLINE integral_padded_<T> zpad(T val, size_t num_digits)107{108return integral_padded_<T>(val, T(10), num_digits);109}110/** pad the argument with zeroes on the left */111template<class T>112C4_ALWAYS_INLINE integral_padded_<T> zpad(integral_<T> val, size_t num_digits)113{114return integral_padded_<T>(val.val, val.radix, num_digits);115}116/** pad the argument with zeroes on the left */117C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(std::nullptr_t, size_t num_digits)118{119return integral_padded_<intptr_t>(0, 16, num_digits);120}121/** pad the argument with zeroes on the left */122template<class T>123C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T const* val, size_t num_digits)124{125return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);126}127template<class T>128C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T * val, size_t num_digits)129{130return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits);131}132133134/** format the pointer as an hexadecimal value */135template<class T>136inline integral_<intptr_t> hex(T * v)137{138return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));139}140/** format the pointer as an hexadecimal value */141template<class T>142inline integral_<intptr_t> hex(T const* v)143{144return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16));145}146/** format null as an hexadecimal value147* @overload hex */148inline integral_<intptr_t> hex(std::nullptr_t)149{150return integral_<intptr_t>(0, intptr_t(16));151}152/** format the integral_ argument as an hexadecimal value153* @overload hex */154template<class T>155inline integral_<T> hex(T v)156{157return integral_<T>(v, T(16));158}159160/** format the pointer as an octal value */161template<class T>162inline integral_<intptr_t> oct(T const* v)163{164return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));165}166/** format the pointer as an octal value */167template<class T>168inline integral_<intptr_t> oct(T * v)169{170return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8));171}172/** format null as an octal value */173inline integral_<intptr_t> oct(std::nullptr_t)174{175return integral_<intptr_t>(intptr_t(0), intptr_t(8));176}177/** format the integral_ argument as an octal value */178template<class T>179inline integral_<T> oct(T v)180{181return integral_<T>(v, T(8));182}183184/** format the pointer as a binary 0-1 value185* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */186template<class T>187inline integral_<intptr_t> bin(T const* v)188{189return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));190}191/** format the pointer as a binary 0-1 value192* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */193template<class T>194inline integral_<intptr_t> bin(T * v)195{196return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2));197}198/** format null as a binary 0-1 value199* @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */200inline integral_<intptr_t> bin(std::nullptr_t)201{202return integral_<intptr_t>(intptr_t(0), intptr_t(2));203}204/** format the integral_ argument as a binary 0-1 value205* @see c4::raw() if you want to use a raw memcpy-based binary dump instead of 0-1 formatting */206template<class T>207inline integral_<T> bin(T v)208{209return integral_<T>(v, T(2));210}211212213template<class T>214struct overflow_checked_215{216static_assert(std::is_integral<T>::value, "range checking only for integral types");217C4_ALWAYS_INLINE overflow_checked_(T &val_) : val(&val_) {}218T *val;219};220template<class T>221C4_ALWAYS_INLINE overflow_checked_<T> overflow_checked(T &val)222{223return overflow_checked_<T>(val);224}225226} // namespace fmt227228/** format an integral_ signed type */229template<typename T>230C4_ALWAYS_INLINE231typename std::enable_if<std::is_signed<T>::value, size_t>::type232to_chars(substr buf, fmt::integral_<T> fmt)233{234return itoa(buf, fmt.val, fmt.radix);235}236/** format an integral_ signed type, pad with zeroes */237template<typename T>238C4_ALWAYS_INLINE239typename std::enable_if<std::is_signed<T>::value, size_t>::type240to_chars(substr buf, fmt::integral_padded_<T> fmt)241{242return itoa(buf, fmt.val, fmt.radix, fmt.num_digits);243}244245/** format an integral_ unsigned type */246template<typename T>247C4_ALWAYS_INLINE248typename std::enable_if<std::is_unsigned<T>::value, size_t>::type249to_chars(substr buf, fmt::integral_<T> fmt)250{251return utoa(buf, fmt.val, fmt.radix);252}253/** format an integral_ unsigned type, pad with zeroes */254template<typename T>255C4_ALWAYS_INLINE256typename std::enable_if<std::is_unsigned<T>::value, size_t>::type257to_chars(substr buf, fmt::integral_padded_<T> fmt)258{259return utoa(buf, fmt.val, fmt.radix, fmt.num_digits);260}261262template<class T>263C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> wrapper)264{265if(C4_LIKELY(!overflows<T>(s)))266return atox(s, wrapper.val);267return false;268}269270271//-----------------------------------------------------------------------------272//-----------------------------------------------------------------------------273//-----------------------------------------------------------------------------274// formatting real types275276namespace fmt {277278template<class T>279struct real_280{281T val;282int precision;283RealFormat_e fmt;284real_(T v, int prec=-1, RealFormat_e f=FTOA_FLOAT) : val(v), precision(prec), fmt(f) {}285};286287template<class T>288real_<T> real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)289{290return real_<T>(val, precision, fmt);291}292293} // namespace fmt294295inline size_t to_chars(substr buf, fmt::real_< float> fmt) { return ftoa(buf, fmt.val, fmt.precision, fmt.fmt); }296inline size_t to_chars(substr buf, fmt::real_<double> fmt) { return dtoa(buf, fmt.val, fmt.precision, fmt.fmt); }297298299//-----------------------------------------------------------------------------300//-----------------------------------------------------------------------------301//-----------------------------------------------------------------------------302// writing raw binary data303304namespace fmt {305306/** @see blob_ */307template<class T>308struct raw_wrapper_ : public blob_<T>309{310size_t alignment;311312C4_ALWAYS_INLINE raw_wrapper_(blob_<T> data, size_t alignment_) noexcept313:314blob_<T>(data),315alignment(alignment_)316{317C4_ASSERT_MSG(alignment > 0 && (alignment & (alignment - 1)) == 0, "alignment must be a power of two");318}319};320321using const_raw_wrapper = raw_wrapper_<cbyte>;322using raw_wrapper = raw_wrapper_<byte>;323324/** mark a variable to be written in raw binary format, using memcpy325* @see blob_ */326inline const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t))327{328return const_raw_wrapper(data, alignment);329}330/** mark a variable to be written in raw binary format, using memcpy331* @see blob_ */332inline const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t))333{334return const_raw_wrapper(data, alignment);335}336/** mark a variable to be written in raw binary format, using memcpy337* @see blob_ */338template<class T>339inline const_raw_wrapper craw(T const& C4_RESTRICT data, size_t alignment=alignof(T))340{341return const_raw_wrapper(cblob(data), alignment);342}343/** mark a variable to be written in raw binary format, using memcpy344* @see blob_ */345template<class T>346inline const_raw_wrapper raw(T const& C4_RESTRICT data, size_t alignment=alignof(T))347{348return const_raw_wrapper(cblob(data), alignment);349}350351/** mark a variable to be read in raw binary format, using memcpy */352inline raw_wrapper raw(blob data, size_t alignment=alignof(max_align_t))353{354return raw_wrapper(data, alignment);355}356/** mark a variable to be read in raw binary format, using memcpy */357template<class T>358inline raw_wrapper raw(T & C4_RESTRICT data, size_t alignment=alignof(T))359{360return raw_wrapper(blob(data), alignment);361}362363} // namespace fmt364365366/** write a variable in raw binary format, using memcpy */367C4CORE_EXPORT size_t to_chars(substr buf, fmt::const_raw_wrapper r);368369/** read a variable in raw binary format, using memcpy */370C4CORE_EXPORT bool from_chars(csubstr buf, fmt::raw_wrapper *r);371/** read a variable in raw binary format, using memcpy */372inline bool from_chars(csubstr buf, fmt::raw_wrapper r)373{374return from_chars(buf, &r);375}376377/** read a variable in raw binary format, using memcpy */378inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper *r)379{380return from_chars(buf, r);381}382/** read a variable in raw binary format, using memcpy */383inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper r)384{385return from_chars(buf, &r);386}387388389//-----------------------------------------------------------------------------390//-----------------------------------------------------------------------------391//-----------------------------------------------------------------------------392// formatting aligned to left/right393394namespace fmt {395396template<class T>397struct left_398{399T val;400size_t width;401char pad;402left_(T v, size_t w, char p) : val(v), width(w), pad(p) {}403};404405template<class T>406struct right_407{408T val;409size_t width;410char pad;411right_(T v, size_t w, char p) : val(v), width(w), pad(p) {}412};413414/** mark an argument to be aligned left */415template<class T>416left_<T> left(T val, size_t width, char padchar=' ')417{418return left_<T>(val, width, padchar);419}420421/** mark an argument to be aligned right */422template<class T>423right_<T> right(T val, size_t width, char padchar=' ')424{425return right_<T>(val, width, padchar);426}427428} // namespace fmt429430431template<class T>432size_t to_chars(substr buf, fmt::left_<T> const& C4_RESTRICT align)433{434size_t ret = to_chars(buf, align.val);435if(ret >= buf.len || ret >= align.width)436return ret > align.width ? ret : align.width;437buf.first(align.width).sub(ret).fill(align.pad);438to_chars(buf, align.val);439return align.width;440}441442template<class T>443size_t to_chars(substr buf, fmt::right_<T> const& C4_RESTRICT align)444{445size_t ret = to_chars(buf, align.val);446if(ret >= buf.len || ret >= align.width)447return ret > align.width ? ret : align.width;448size_t rem = static_cast<size_t>(align.width - ret);449buf.first(rem).fill(align.pad);450to_chars(buf.sub(rem), align.val);451return align.width;452}453454455//-----------------------------------------------------------------------------456//-----------------------------------------------------------------------------457//-----------------------------------------------------------------------------458459/// @cond dev460// terminates the variadic recursion461inline size_t cat(substr /*buf*/)462{463return 0;464}465/// @endcond466467468/** serialize the arguments, concatenating them to the given fixed-size buffer.469* The buffer size is strictly respected: no writes will occur beyond its end.470* @return the number of characters needed to write all the arguments into the buffer.471* @see c4::catrs() if instead of a fixed-size buffer, a resizeable container is desired472* @see c4::uncat() for the inverse function473* @see c4::catsep() if a separator between each argument is to be used474* @see c4::format() if a format string is desired */475template<class Arg, class... Args>476size_t cat(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)477{478size_t num = to_chars(buf, a);479buf = buf.len >= num ? buf.sub(num) : substr{};480num += cat(buf, more...);481return num;482}483484/** like c4::cat() but return a substr instead of a size */485template<class... Args>486substr cat_sub(substr buf, Args && ...args)487{488size_t sz = cat(buf, std::forward<Args>(args)...);489C4_CHECK(sz <= buf.len);490return {buf.str, sz <= buf.len ? sz : buf.len};491}492493494//-----------------------------------------------------------------------------495496/// @cond dev497// terminates the variadic recursion498inline size_t uncat(csubstr /*buf*/)499{500return 0;501}502/// @endcond503504505/** deserialize the arguments from the given buffer.506*507* @return the number of characters read from the buffer, or csubstr::npos508* if a conversion was not successful.509* @see c4::cat(). c4::uncat() is the inverse of c4::cat(). */510template<class Arg, class... Args>511size_t uncat(csubstr buf, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)512{513size_t out = from_chars_first(buf, &a);514if(C4_UNLIKELY(out == csubstr::npos))515return csubstr::npos;516buf = buf.len >= out ? buf.sub(out) : substr{};517size_t num = uncat(buf, more...);518if(C4_UNLIKELY(num == csubstr::npos))519return csubstr::npos;520return out + num;521}522523524525//-----------------------------------------------------------------------------526//-----------------------------------------------------------------------------527//-----------------------------------------------------------------------------528529namespace detail {530531template<class Sep>532C4_ALWAYS_INLINE size_t catsep_more(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)533{534return 0;535}536537template<class Sep, class Arg, class... Args>538size_t catsep_more(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)539{540size_t ret = to_chars(buf, sep);541size_t num = ret;542buf = buf.len >= ret ? buf.sub(ret) : substr{};543ret = to_chars(buf, a);544num += ret;545buf = buf.len >= ret ? buf.sub(ret) : substr{};546ret = catsep_more(buf, sep, more...);547num += ret;548return num;549}550551552template<class Sep>553inline size_t uncatsep_more(csubstr /*buf*/, Sep & /*sep*/)554{555return 0;556}557558template<class Sep, class Arg, class... Args>559size_t uncatsep_more(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)560{561size_t ret = from_chars_first(buf, &sep);562size_t num = ret;563if(C4_UNLIKELY(ret == csubstr::npos))564return csubstr::npos;565buf = buf.len >= ret ? buf.sub(ret) : substr{};566ret = from_chars_first(buf, &a);567if(C4_UNLIKELY(ret == csubstr::npos))568return csubstr::npos;569num += ret;570buf = buf.len >= ret ? buf.sub(ret) : substr{};571ret = uncatsep_more(buf, sep, more...);572if(C4_UNLIKELY(ret == csubstr::npos))573return csubstr::npos;574num += ret;575return num;576}577578} // namespace detail579580/// @cond dev581template<class Sep>582size_t catsep(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/)583{584return 0;585}586/// @endcond587588/** serialize the arguments, concatenating them to the given fixed-size589* buffer, using a separator between each argument.590* The buffer size is strictly respected: no writes will occur beyond its end.591* @return the number of characters needed to write all the arguments into the buffer.592* @see c4::catseprs() if instead of a fixed-size buffer, a resizeable container is desired593* @see c4::uncatsep() for the inverse function (ie, reading instead of writing)594* @see c4::cat() if no separator is needed595* @see c4::format() if a format string is desired */596template<class Sep, class Arg, class... Args>597size_t catsep(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)598{599size_t num = to_chars(buf, a);600buf = buf.len >= num ? buf.sub(num) : substr{};601num += detail::catsep_more(buf, sep, more...);602return num;603}604605/** like c4::catsep() but return a substr instead of a size606* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */607template<class... Args>608substr catsep_sub(substr buf, Args && ...args)609{610size_t sz = catsep(buf, std::forward<Args>(args)...);611C4_CHECK(sz <= buf.len);612return {buf.str, sz <= buf.len ? sz : buf.len};613}614615/** deserialize the arguments from the given buffer, using a separator.616*617* @return the number of characters read from the buffer, or csubstr::npos618* if a conversion was not successful619* @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */620template<class Sep, class Arg, class... Args>621size_t uncatsep(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)622{623size_t ret = from_chars_first(buf, &a), num = ret;624if(C4_UNLIKELY(ret == csubstr::npos))625return csubstr::npos;626buf = buf.len >= ret ? buf.sub(ret) : substr{};627ret = detail::uncatsep_more(buf, sep, more...);628if(C4_UNLIKELY(ret == csubstr::npos))629return csubstr::npos;630num += ret;631return num;632}633634635//-----------------------------------------------------------------------------636//-----------------------------------------------------------------------------637//-----------------------------------------------------------------------------638639/// @cond dev640// terminates the variadic recursion641inline size_t format(substr buf, csubstr fmt)642{643return to_chars(buf, fmt);644}645/// @endcond646647648/** using a format string, serialize the arguments into the given649* fixed-size buffer.650* The buffer size is strictly respected: no writes will occur beyond its end.651* In the format string, each argument is marked with a compact652* curly-bracket pair: {}. Arguments beyond the last curly bracket pair653* are silently ignored. For example:654* @code{.cpp}655* c4::format(buf, "the {} drank {} {}", "partier", 5, "beers"); // the partier drank 5 beers656* c4::format(buf, "the {} drank {} {}", "programmer", 6, "coffees"); // the programmer drank 6 coffees657* @endcode658* @return the number of characters needed to write into the buffer.659* @see c4::formatrs() if instead of a fixed-size buffer, a resizeable container is desired660* @see c4::unformat() for the inverse function661* @see c4::cat() if no format or separator is needed662* @see c4::catsep() if no format is needed, but a separator must be used */663template<class Arg, class... Args>664size_t format(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more)665{666size_t pos = fmt.find("{}"); // @todo use _find_fmt()667if(C4_UNLIKELY(pos == csubstr::npos))668return to_chars(buf, fmt);669size_t num = to_chars(buf, fmt.sub(0, pos));670size_t out = num;671buf = buf.len >= num ? buf.sub(num) : substr{};672num = to_chars(buf, a);673out += num;674buf = buf.len >= num ? buf.sub(num) : substr{};675num = format(buf, fmt.sub(pos + 2), more...);676out += num;677return out;678}679680/** like c4::format() but return a substr instead of a size681* @see c4::format()682* @see c4::catsep(). uncatsep() is the inverse of catsep(). */683template<class... Args>684substr format_sub(substr buf, csubstr fmt, Args const& C4_RESTRICT ...args)685{686size_t sz = c4::format(buf, fmt, args...);687C4_CHECK(sz <= buf.len);688return {buf.str, sz <= buf.len ? sz : buf.len};689}690691692//-----------------------------------------------------------------------------693694/// @cond dev695// terminates the variadic recursion696inline size_t unformat(csubstr /*buf*/, csubstr fmt)697{698return fmt.len;699}700/// @endcond701702703/** using a format string, deserialize the arguments from the given704* buffer.705* @return the number of characters read from the buffer, or npos if a conversion failed.706* @see c4::format(). c4::unformat() is the inverse function to format(). */707template<class Arg, class... Args>708size_t unformat(csubstr buf, csubstr fmt, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more)709{710const size_t pos = fmt.find("{}");711if(C4_UNLIKELY(pos == csubstr::npos))712return unformat(buf, fmt);713size_t num = pos;714size_t out = num;715buf = buf.len >= num ? buf.sub(num) : substr{};716num = from_chars_first(buf, &a);717if(C4_UNLIKELY(num == csubstr::npos))718return csubstr::npos;719out += num;720buf = buf.len >= num ? buf.sub(num) : substr{};721num = unformat(buf, fmt.sub(pos + 2), more...);722if(C4_UNLIKELY(num == csubstr::npos))723return csubstr::npos;724out += num;725return out;726}727728729//-----------------------------------------------------------------------------730//-----------------------------------------------------------------------------731//-----------------------------------------------------------------------------732733/** like c4::cat(), but receives a container, and resizes it as needed to contain734* the result. The container is overwritten. To append to it, use the append735* overload.736* @see c4::cat() */737template<class CharOwningContainer, class... Args>738inline void catrs(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)739{740retry:741substr buf = to_substr(*cont);742size_t ret = cat(buf, args...);743cont->resize(ret);744if(ret > buf.len)745goto retry;746}747748/** like c4::cat(), but creates and returns a new container sized as needed to contain749* the result.750* @see c4::cat() */751template<class CharOwningContainer, class... Args>752inline CharOwningContainer catrs(Args const& C4_RESTRICT ...args)753{754CharOwningContainer cont;755catrs(&cont, args...);756return cont;757}758759/** like c4::cat(), but receives a container, and appends to it instead of760* overwriting it. The container is resized as needed to contain the result.761* @return the region newly appended to the original container762* @see c4::cat()763* @see c4::catrs() */764template<class CharOwningContainer, class... Args>765inline csubstr catrs_append(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args)766{767const size_t pos = cont->size();768retry:769substr buf = to_substr(*cont).sub(pos);770size_t ret = cat(buf, args...);771cont->resize(pos + ret);772if(ret > buf.len)773goto retry;774return to_csubstr(*cont).range(pos, cont->size());775}776777778//-----------------------------------------------------------------------------779780/** like c4::catsep(), but receives a container, and resizes it as needed to contain the result.781* The container is overwritten. To append to the container use the append overload.782* @see c4::catsep() */783template<class CharOwningContainer, class Sep, class... Args>784inline void catseprs(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)785{786retry:787substr buf = to_substr(*cont);788size_t ret = catsep(buf, sep, args...);789cont->resize(ret);790if(ret > buf.len)791goto retry;792}793794/** like c4::catsep(), but create a new container with the result.795* @return the requested container */796template<class CharOwningContainer, class Sep, class... Args>797inline CharOwningContainer catseprs(Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)798{799CharOwningContainer cont;800catseprs(&cont, sep, args...);801return cont;802}803804805/** like catsep(), but receives a container, and appends the arguments, resizing the806* container as needed to contain the result. The buffer is appended to.807* @return a csubstr of the appended part808* @ingroup formatting_functions */809template<class CharOwningContainer, class Sep, class... Args>810inline csubstr catseprs_append(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)811{812const size_t pos = cont->size();813retry:814substr buf = to_substr(*cont).sub(pos);815size_t ret = catsep(buf, sep, args...);816cont->resize(pos + ret);817if(ret > buf.len)818goto retry;819return to_csubstr(*cont).range(pos, cont->size());820}821822823//-----------------------------------------------------------------------------824825/** like c4::format(), but receives a container, and resizes it as needed826* to contain the result. The container is overwritten. To append to827* the container use the append overload.828* @see c4::format() */829template<class CharOwningContainer, class... Args>830inline void formatrs(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)831{832retry:833substr buf = to_substr(*cont);834size_t ret = format(buf, fmt, args...);835cont->resize(ret);836if(ret > buf.len)837goto retry;838}839840/** like c4::format(), but create a new container with the result.841* @return the requested container */842template<class CharOwningContainer, class... Args>843inline CharOwningContainer formatrs(csubstr fmt, Args const& C4_RESTRICT ...args)844{845CharOwningContainer cont;846formatrs(&cont, fmt, args...);847return cont;848}849850/** like format(), but receives a container, and appends the851* arguments, resizing the container as needed to contain the852* result. The buffer is appended to.853* @return the region newly appended to the original container854* @ingroup formatting_functions */855template<class CharOwningContainer, class... Args>856inline csubstr formatrs_append(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)857{858const size_t pos = cont->size();859retry:860substr buf = to_substr(*cont).sub(pos);861size_t ret = format(buf, fmt, args...);862cont->resize(pos + ret);863if(ret > buf.len)864goto retry;865return to_csubstr(*cont).range(pos, cont->size());866}867868} // namespace c4869870#ifdef _MSC_VER871# pragma warning(pop)872#elif defined(__clang__)873# pragma clang diagnostic pop874#elif defined(__GNUC__)875# pragma GCC diagnostic pop876#endif877878#endif /* _C4_FORMAT_HPP_ */879880881