// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_FMT_H /// Typesafe format strings from formal grammars & standard RTTI namespace ircd::fmt { IRCD_EXCEPTION(ircd::error, error); IRCD_PANICKING(error, invalid_format); IRCD_PANICKING(error, invalid_type); IRCD_PANICKING(error, illegal); struct sprintf; struct vsprintf; struct snprintf; struct vsnprintf; struct snstringf; struct vsnstringf; struct literal_snstringf; struct literal_snstringf__max; template struct bsprintf; using arg = std::tuple; literal_snstringf operator ""_snstringf(const char *text, const size_t len); literal_snstringf__max operator,(const literal_snstringf, const ulong max); template std::string operator,(const literal_snstringf__max, args&&...); } namespace ircd { using fmt::operator ""_snstringf; } /// Typesafe snprintf() from formal grammar and RTTI. /// /// This function accepts a format string and a variable number of arguments /// composing formatted null-terminated output in the provided output buffer. /// The format string is compliant with standard snprintf() (TODO: not yet). /// The type information of the arguments is grabbed from the variadic template /// and references are passed to the formal output grammars. This means you can /// pass an std::string directly without calling c_str(), as well as pass a /// non-null-terminated string_view safely. /// class ircd::fmt::snprintf { window_buffer out; // Window on the output buffer. const_buffer fmt; // Current running position in the fmtstr. short idx; // Keeps count of the args for better err msgs bool finished() const noexcept; size_t remaining() const noexcept; size_t consumed() const { return out.consumed(); } string_view completed() const { return out.completed(); } void append(const string_view &); void argument(const arg &); protected: IRCD_OVERLOAD(internal) snprintf(internal_t, const mutable_buffer &, const string_view &, const va_rtti &); public: operator ssize_t() const { return consumed(); } operator string_view() const { return completed(); } template snprintf(char *const &buf, const size_t &max, const string_view &fmt, Args&&... args) :snprintf { internal, mutable_buffer{buf, max}, fmt, va_rtti{std::forward(args)...} }{} }; /// Same as snprintf() where n= is deduced by the buffer size argument /// struct ircd::fmt::sprintf :snprintf { template sprintf(const mutable_buffer &buf, const string_view &fmt, Args&&... args) :snprintf { internal, buf, fmt, va_rtti{std::forward(args)...} }{} }; /// A complement to fmt::snprintf() accepting an already-made va_rtti. /// /// This function has no variadic template; instead it accepts the type /// which would be composed by such a variadic template called /// ircd::va_rtti directly. /// /// ircd::va_rtti is a lightweight pairing of argument pointers to runtime /// type indexes. ircd::va_rtti is not a template itself because its purpose /// is to bring this type information out of the header files to where the /// grammar is instantiated. /// struct ircd::fmt::vsnprintf :snprintf { vsnprintf(char *const &buf, const size_t &max, const string_view &fmt, const va_rtti &ap) :snprintf { internal, mutable_buffer{buf, max}, fmt, ap }{} }; /// Same as vsnprintf() where n is deduced from the buffer size. /// struct ircd::fmt::vsprintf :snprintf { vsprintf(const mutable_buffer &buf, const string_view &fmt, const va_rtti &ap) :snprintf { internal, buf, fmt, ap }{} }; /// Allocates a buffer (std::string) of size for the result. /// /// see snstringf() for vararg format overload. struct ircd::fmt::vsnstringf :std::string { vsnstringf(const size_t &max, const string_view &fmt, const va_rtti &ap) :std::string { ircd::string(max, [&fmt, &ap] (const mutable_buffer &buf) -> string_view { return vsprintf { buf, fmt, ap }; }) }{} }; /// Allocates a buffer (std::string) of size for the result. /// /// Note: that OR'ing the size with ircd::SHRINK_TO_FIT constant will trigger /// a standard string re-evaluation to determine if the final result string /// should be transferred to a smaller buffer. struct ircd::fmt::snstringf :vsnstringf { template snstringf(const size_t &max, const string_view &fmt, args&&... a) :vsnstringf { max, fmt, va_rtti{std::forward(a)...} }{} }; /// Efficient in-place buffer convenience template. /// /// This is an object which contains the result buffer internally. The size /// of that buffer is given in the template parameter. This is clean and /// efficient for basic format string operations on the stack while avoiding /// the clutter of buffer statements and mgmt. template struct ircd::fmt::bsprintf :snprintf ,string_view { char buf[MAX]; template bsprintf(const string_view &fmt, args&&... a) :snprintf { internal, buf, fmt, va_rtti{std::forward(a)...} } ,string_view { buf, size_t(static_cast(*this)) }{} }; /// Experimental typedef for format strings. struct ircd::fmt::literal_snstringf { string_view format; }; /// Experimental typedef for format strings. struct ircd::fmt::literal_snstringf__max { literal_snstringf format; size_t max; }; template inline std::string ircd::fmt::operator,(const literal_snstringf__max format, args&&... a) { return fmt::snstringf { format.max, format.format.format, std::forward(a)... }; } inline ircd::fmt::literal_snstringf__max ircd::fmt::operator,(const literal_snstringf format, const ulong max) { return literal_snstringf__max { format, max }; } inline ircd::fmt::literal_snstringf ircd::fmt::operator ""_snstringf(const char *const format, const size_t len) { return literal_snstringf { string_view { format, len } }; }