[beast: 11/57] SFI: add StringFormatter for type safe printf-like formatting in C++11
- From: Tim Janik <timj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [beast: 11/57] SFI: add StringFormatter for type safe printf-like formatting in C++11
- Date: Sun, 23 Jul 2017 09:58:43 +0000 (UTC)
commit f3cdbfb6fb2774355c95e327ce05c5b705499946
Author: Tim Janik <timj gnu org>
Date: Fri Jul 14 03:05:08 2017 +0200
SFI: add StringFormatter for type safe printf-like formatting in C++11
Signed-off-by: Tim Janik <timj gnu org>
sfi/formatter.cc | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
sfi/formatter.hh | 169 ++++++++++++++++++
2 files changed, 678 insertions(+), 0 deletions(-)
---
diff --git a/sfi/formatter.cc b/sfi/formatter.cc
new file mode 100644
index 0000000..7db75e9
--- /dev/null
+++ b/sfi/formatter.cc
@@ -0,0 +1,509 @@
+// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
+#include "formatter.hh"
+#include <unistd.h> // isatty
+#include <cstring>
+
+/** @TODO:
+ * - StringFormatter: support directives: %%n %%S %%ls
+ * - StringFormatter: support directive flags: I
+ */
+
+#ifdef __clang__ // clang++-3.8.0: work around 'variable length array of non-POD element type'
+#define CC_DECLARE_VLA(Type, var, count) std::vector<Type> var (count)
+#else // sane c++
+#define CC_DECLARE_VLA(Type, var, count) Type var[count]
+#endif
+
+namespace Bse {
+namespace Lib {
+
+template<class... Args> static std::string
+system_string_printf (const char *format, Args... args)
+{
+ char *cstr = NULL;
+ int ret = asprintf (&cstr, format, args...);
+ if (ret >= 0 && cstr)
+ {
+ std::string result = cstr;
+ free (cstr);
+ return result;
+ }
+ return format;
+}
+
+static bool
+parse_unsigned_integer (const char **stringp, uint64_t *up)
+{ // '0' | [1-9] [0-9]* : <= 18446744073709551615
+ const char *p = *stringp;
+ // zero
+ if (*p == '0' && !(p[1] >= '0' && p[1] <= '9'))
+ {
+ *up = 0;
+ *stringp = p + 1;
+ return true;
+ }
+ // first digit
+ if (!(*p >= '1' && *p <= '9'))
+ return false;
+ uint64_t u = *p - '0';
+ p++;
+ // rest digits
+ while (*p >= '0' && *p <= '9')
+ {
+ const uint64_t last = u;
+ u = u * 10 + (*p - '0');
+ p++;
+ if (u < last) // overflow
+ return false;
+ }
+ *up = u;
+ *stringp = p;
+ return true;
+}
+
+static bool
+parse_positional (const char **stringp, uint64_t *ap)
+{ // [0-9]+ '$'
+ const char *p = *stringp;
+ uint64_t ui64 = 0;
+ if (parse_unsigned_integer (&p, &ui64) && *p == '$')
+ {
+ p++;
+ *ap = ui64;
+ *stringp = p;
+ return true;
+ }
+ return false;
+}
+
+const char*
+StringFormatter::parse_directive (const char **stringp, size_t *indexp, Directive *dirp)
+{ // '%' positional? [-+#0 '']* ([0-9]*|[*]positional?) ([.]([0-9]*|[*]positional?))? [hlLjztqZ]*
[spmcCdiouXxFfGgEeAa]
+ const char *p = *stringp;
+ size_t index = *indexp;
+ Directive fdir;
+ // '%' directive start
+ if (*p != '%')
+ return "missing '%' at start";
+ p++;
+ // positional argument
+ uint64_t ui64 = -1;
+ if (parse_positional (&p, &ui64))
+ {
+ if (ui64 > 0 && ui64 <= 2147483647)
+ fdir.value_index = ui64;
+ else
+ return "invalid positional specification";
+ }
+ // flags
+ const char *flags = "-+#0 '";
+ while (strchr (flags, *p))
+ switch (*p)
+ {
+ case '-': fdir.adjust_left = true; goto default_case;
+ case '+': fdir.add_sign = true; goto default_case;
+ case '#': fdir.alternate_form = true; goto default_case;
+ case '0': fdir.zero_padding = true; goto default_case;
+ case ' ': fdir.add_space = true; goto default_case;
+ case '\'': fdir.locale_grouping = true; goto default_case;
+ default: default_case:
+ p++;
+ break;
+ }
+ // field width
+ ui64 = 0;
+ if (*p == '*')
+ {
+ p++;
+ if (parse_positional (&p, &ui64))
+ {
+ if (ui64 > 0 && ui64 <= 2147483647)
+ fdir.width_index = ui64;
+ else
+ return "invalid positional specification";
+ }
+ else
+ fdir.width_index = index++;
+ fdir.use_width = true;
+ }
+ else if (parse_unsigned_integer (&p, &ui64))
+ {
+ if (ui64 <= 2147483647)
+ fdir.field_width = ui64;
+ else
+ return "invalid field width specification";
+ fdir.use_width = true;
+ }
+ // precision
+ if (*p == '.')
+ {
+ fdir.use_precision = true;
+ p++;
+ }
+ if (*p == '*')
+ {
+ p++;
+ if (parse_positional (&p, &ui64))
+ {
+ if (ui64 > 0 && ui64 <= 2147483647)
+ fdir.precision_index = ui64;
+ else
+ return "invalid positional specification";
+ }
+ else
+ fdir.precision_index = index++;
+ }
+ else if (parse_unsigned_integer (&p, &ui64))
+ {
+ if (ui64 <= 2147483647)
+ fdir.precision = ui64;
+ else
+ return "invalid precision specification";
+ }
+ // modifiers
+ const char *modifiers = "hlLjztqZ";
+ while (strchr (modifiers, *p))
+ p++;
+ // conversion
+ const char *conversion = "dioucCspmXxEeFfGgAa%";
+ if (!strchr (conversion, *p))
+ return "missing conversion specifier";
+ if (fdir.value_index == 0 && !strchr ("m%", *p))
+ fdir.value_index = index++;
+ fdir.conversion = *p++;
+ if (fdir.conversion == 'C') // %lc in SUSv2
+ fdir.conversion = 'c';
+ // success
+ *dirp = fdir;
+ *indexp = index;
+ *stringp = p;
+ return NULL; // OK
+}
+
+const StringFormatter::FormatArg&
+StringFormatter::format_arg (size_t nth)
+{
+ if (nth && nth <= nargs_)
+ return fargs_[nth-1];
+ static const FormatArg zero_arg = { { 0, }, 0 };
+ return zero_arg;
+}
+
+StringFormatter::LDouble
+StringFormatter::arg_as_ldouble (size_t nth)
+{
+ const FormatArg &farg = format_arg (nth);
+ switch (farg.kind)
+ {
+ case '1': return farg.i1;
+ case '2': return farg.i2;
+ case '4': return farg.i4;
+ case '6': return farg.i6;
+ case '8': return farg.i8;
+ case 'f': return farg.f;
+ case 'd': return farg.d;
+ case 'p': return ULLong (farg.p);
+ case 's': return ULLong (farg.s);
+ default: return 0;
+ }
+}
+
+const char*
+StringFormatter::arg_as_chars (size_t nth)
+{
+ if (!(nth && nth <= nargs_))
+ return "";
+ if ((fargs_[nth-1].kind == 's' || fargs_[nth-1].kind == 'p') && fargs_[nth-1].p == NULL)
+ return "(null)";
+ return fargs_[nth-1].kind != 's' ? "" : fargs_[nth-1].s;
+}
+
+void*
+StringFormatter::arg_as_ptr (size_t nth)
+{
+ if (!(nth && nth <= nargs_))
+ return NULL;
+ return fargs_[nth-1].p;
+}
+
+StringFormatter::LLong
+StringFormatter::arg_as_longlong (size_t nth)
+{
+ const FormatArg &farg = format_arg (nth);
+ switch (farg.kind)
+ {
+ case '1': return farg.i1;
+ case '2': return farg.i2;
+ case '4': return farg.i4;
+ case '6': return farg.i6;
+ case '8': return farg.i8;
+ case 'f': return farg.f;
+ case 'd': return farg.d;
+ case 'p': return LLong (farg.p);
+ case 's': return LLong (farg.s);
+ default: return 0;
+ }
+}
+
+uint32_t
+StringFormatter::arg_as_width (size_t nth)
+{
+ int32_t w = arg_as_longlong (nth);
+ w = std::abs (w);
+ return w < 0 ? std::abs (w + 1) : w; // turn -2147483648 into +2147483647
+}
+
+uint32_t
+StringFormatter::arg_as_precision (size_t nth)
+{
+ const int32_t precision = arg_as_longlong (nth);
+ return std::max (0, precision);
+}
+
+template<class Arg> std::string
+StringFormatter::render_arg (const Directive &dir, const char *modifier, Arg arg)
+{
+ std::string result, format;
+ const int field_width = !dir.use_width || !dir.width_index ? dir.field_width : arg_as_width
(dir.width_index);
+ const int field_precision = !dir.use_precision || !dir.precision_index ? std::max (uint32_t (0),
dir.precision) : arg_as_precision (dir.precision_index);
+ // format directive
+ format += '%';
+ if (dir.adjust_left)
+ format += '-';
+ if (dir.add_sign)
+ format += '+';
+ if (dir.add_space)
+ format += ' ';
+ if (dir.zero_padding && !dir.adjust_left&& strchr ("diouXx" "FfGgEeAa", dir.conversion))
+ format += '0';
+ if (dir.alternate_form && strchr ("oXx" "FfGgEeAa", dir.conversion))
+ format += '#';
+ if (dir.locale_grouping && strchr ("idu" "FfGg", dir.conversion))
+ format += '\'';
+ if (dir.use_width)
+ format += '*';
+ if (dir.use_precision && strchr ("sm" "diouXx" "FfGgEeAa", dir.conversion)) // !cp
+ format += ".*";
+ if (modifier)
+ format += modifier;
+ format += dir.conversion;
+ // printf formatting
+ if (dir.use_width && dir.use_precision)
+ return system_string_printf (format.c_str(), field_width, field_precision, arg);
+ else if (dir.use_precision)
+ return system_string_printf (format.c_str(), field_precision, arg);
+ else if (dir.use_width)
+ return system_string_printf (format.c_str(), field_width, arg);
+ else
+ return system_string_printf (format.c_str(), arg);
+}
+
+std::string
+StringFormatter::render_directive (const Directive &dir)
+{
+ switch (dir.conversion)
+ {
+ case 'm':
+ return render_arg (dir, "", int (0)); // dummy arg to silence compiler
+ case 'p':
+ return render_arg (dir, "", arg_as_ptr (dir.value_index));
+ case 's': // precision
+ return render_arg (dir, "", arg_as_chars (dir.value_index));
+ case 'c': case 'd': case 'i': case 'o': case 'u': case 'X': case 'x':
+ switch (format_arg (dir.value_index).kind)
+ {
+ case '1': return render_arg (dir, "hh", format_arg (dir.value_index).i1);
+ case '2': return render_arg (dir, "h", format_arg (dir.value_index).i2);
+ case '4': return render_arg (dir, "", format_arg (dir.value_index).i4);
+ case '6': return render_arg (dir, "l", format_arg (dir.value_index).i6);
+ case '8': return render_arg (dir, "ll", format_arg (dir.value_index).i8);
+ default: return render_arg (dir, "ll", arg_as_longlong (dir.value_index));
+ }
+ case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A':
+ switch (format_arg (dir.value_index).kind)
+ {
+ case 'f': return render_arg (dir, "", format_arg (dir.value_index).f);
+ case 'd':
+ default: return render_arg (dir, "L", arg_as_ldouble (dir.value_index));
+ }
+ case '%':
+ return "%";
+ }
+ return std::string ("%") + dir.conversion;
+}
+
+static inline size_t
+upper_directive_count (const char *format)
+{
+ size_t n = 0;
+ for (const char *p = format; *p; p++)
+ if (p[0] == '%') // count %...
+ {
+ n++;
+ if (p[1] == '%') // dont count %% twice
+ p++;
+ }
+ return n;
+}
+
+std::string
+StringFormatter::render_format (const size_t last, const char *format)
+{
+ if (last != nargs_)
+ { // should never be reached
+ fputs (__FILE__ ": template argument list unpacking failed\n", stderr);
+ return "";
+ }
+ // allocate enough space to hold all directives possibly contained in format
+ const size_t max_dirs = 1 + upper_directive_count (format);
+ CC_DECLARE_VLA (Directive, fdirs, max_dirs); // Directive fdirs[max_dirs];
+ // parse format into Directive stack
+ size_t nextarg = 1, ndirs = 0;
+ const char *p = format;
+ while (*p)
+ {
+ do
+ {
+ if (p[0] == '%')
+ break;
+ p++;
+ }
+ while (*p);
+ if (*p == 0)
+ break;
+ const size_t start = p - format;
+ const char *err = parse_directive (&p, &nextarg, &fdirs[ndirs]);
+ if (err)
+ return format_error (err, format, ndirs + 1);
+ fdirs[ndirs].start = start;
+ fdirs[ndirs].end = p - format;
+ ndirs++;
+ if (!(ndirs < max_dirs))
+ { // should never be reached
+ fputs (__FILE__ ": invalid result from upper_directive_count()", stderr);
+ return "";
+ }
+ }
+ const size_t argcounter = nextarg - 1;
+ fdirs[ndirs].end = fdirs[ndirs].start = p - format;
+ // check maximum argument reference and argument count
+ size_t argmaxref = argcounter;
+ for (size_t i = 0; i < ndirs; i++)
+ {
+ const Directive &fdir = fdirs[i];
+ argmaxref = std::max (argmaxref, size_t (fdir.value_index));
+ argmaxref = std::max (argmaxref, size_t (fdir.width_index));
+ argmaxref = std::max (argmaxref, size_t (fdir.precision_index));
+ }
+ if (argmaxref > last)
+ return format_error ("too few arguments for format", format, 0);
+ if (argmaxref < last)
+ return format_error ("too many arguments for format", format, 0);
+ // format pieces
+ std::string result;
+ p = format;
+ for (size_t i = 0; i <= ndirs; i++)
+ {
+ const Directive &fdir = fdirs[i];
+ result += std::string (p, fdir.start - (p - format));
+ if (fdir.conversion)
+ {
+ std::string rendered_arg = render_directive (fdir);
+ if (arg_transform_)
+ rendered_arg = arg_transform_ (rendered_arg);
+ result += rendered_arg;
+ }
+ p = format + fdir.end;
+ }
+ return result;
+}
+
+std::string
+StringFormatter::locale_format (const size_t last, const char *format)
+{
+ if (locale_context_ == CURRENT_LOCALE)
+ return render_format (last, format);
+ else
+ {
+ ScopedPosixLocale posix_locale_scope; // pushes POSIX locale for this scope
+ return render_format (last, format);
+ }
+}
+
+std::string
+StringFormatter::format_error (const char *err, const char *format, size_t directive)
+{
+ const char *cyan = "", *cred = "", *cyel = "", *crst = "";
+ if (isatty (fileno (stderr)))
+ {
+ const char *term = getenv ("TERM");
+ if (term && strcmp (term, "dumb") != 0)
+ {
+ cyan = "\033[36m";
+ cred = "\033[31m\033[1m";
+ cyel = "\033[33m";
+ crst = "\033[39m\033[22m";
+ }
+ }
+ if (directive)
+ fprintf (stderr, "%sStringFormatter: %sWARNING:%s%s %s in directive %zu:%s %s\n", cyan, cred, crst,
cyel, err, directive, crst, format);
+ else
+ fprintf (stderr, "%sStringFormatter: %sWARNING:%s%s %s:%s %s\n", cyan, cred, crst, cyel, err, crst,
format);
+ return format;
+}
+
+ScopedLocale::ScopedLocale (locale_t scope_locale) :
+ locale_ (NULL)
+{
+ if (!scope_locale)
+ locale_ = uselocale (LC_GLOBAL_LOCALE); // use process locale
+ else
+ locale_ = uselocale (scope_locale); // use custom locale
+ if (locale_ == NULL)
+ fprintf (stderr, "%s: WARNING: uselocale() returned NULL\n", __FILE__);
+}
+
+ScopedLocale::~ScopedLocale ()
+{
+ uselocale (locale_); // restore locale
+}
+
+#if 0
+ScopedLocale::ScopedLocale (const String &locale_name = "")
+{
+ /* this constructor should:
+ * - uselocale (LC_GLOBAL_LOCALE) if locale_name == "",
+ * - create newlocale from locale_name, use it and later delete it, but:
+ * - freelocale(newlocale()) seems buggy on glibc-2.7 (crashes)
+ */
+}
+#endif
+
+ScopedPosixLocale::ScopedPosixLocale () :
+ ScopedLocale (posix_locale())
+{}
+
+locale_t
+ScopedPosixLocale::posix_locale ()
+{
+ static locale_t cached_posix_locale = [] () {
+ locale_t posix_locale = NULL;
+ if (!posix_locale)
+ posix_locale = newlocale (LC_ALL_MASK, "POSIX.UTF-8", NULL);
+ if (!posix_locale)
+ posix_locale = newlocale (LC_ALL_MASK, "C.UTF-8", NULL);
+ if (!posix_locale)
+ posix_locale = newlocale (LC_ALL_MASK, "POSIX", NULL);
+ if (!posix_locale)
+ posix_locale = newlocale (LC_ALL_MASK, "C", NULL);
+ if (!posix_locale)
+ posix_locale = newlocale (LC_ALL_MASK, NULL, NULL);
+ if (posix_locale == NULL)
+ fprintf (stderr, "%s: WARNING: newlocale() returned NULL\n", __FILE__);
+ return posix_locale;
+ } ();
+ return cached_posix_locale;
+}
+
+} // Lib
+} // Bse
diff --git a/sfi/formatter.hh b/sfi/formatter.hh
new file mode 100644
index 0000000..9b15fad
--- /dev/null
+++ b/sfi/formatter.hh
@@ -0,0 +1,169 @@
+// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
+#ifndef __BSE_FORMATTER_HH__
+#define __BSE_FORMATTER_HH__
+
+#include <string>
+#include <sstream>
+#include <functional>
+#include <vector>
+
+namespace Bse {
+
+/// Namespace for implementation internals
+namespace Lib {
+
+/// Class to push a specific locale_t for the scope of its lifetime.
+class ScopedLocale {
+ locale_t locale_;
+ /*copy*/ ScopedLocale (const ScopedLocale&) = delete;
+ ScopedLocale& operator= (const ScopedLocale&) = delete;
+protected:
+ explicit ScopedLocale (locale_t scope_locale);
+public:
+ // explicit ScopedLocale (const String &locale_name = ""); // not supported
+ /*dtor*/ ~ScopedLocale ();
+};
+
+/// Class to push the POSIX/C locale_t (UTF-8) for the scope of its lifetime.
+class ScopedPosixLocale : public ScopedLocale {
+public:
+ explicit ScopedPosixLocale ();
+ static locale_t posix_locale (); ///< Retrieve the (UTF-8) POSIX/C locale_t.
+};
+
+// == StringFormatter ==
+
+/** StringFormatter - sprintf() like string formatting for C++.
+ *
+ * See format() for supported flags, modifiers and conversions.
+ * To find source code strings with size modifiers for possible cleanups, use:
+ * egrep "\"([^\"]|\\\")*%[0-9$]*[-+#0 \'I]*[*0-9$]*[.*0-9$]*[hlLqjzt]+[nSspmCcdiouXxFfGgEeAa]"
+ */
+class StringFormatter {
+ typedef long long signed int LLong;
+ typedef long long unsigned int ULLong;
+ typedef long double LDouble;
+ struct FormatArg {
+ union { LDouble d; double f; signed char i1; short i2; int i4; long i6; LLong i8; void *p; const char
*s; };
+ char kind; // f d i u p s
+ };
+ inline void assign (FormatArg &farg, bool arg) { farg.kind = '1'; farg.i1 = arg; }
+ inline void assign (FormatArg &farg, char arg) { farg.kind = '1'; farg.i1 = arg; }
+ inline void assign (FormatArg &farg, signed char arg) { farg.kind = '1'; farg.i1 = arg; }
+ inline void assign (FormatArg &farg, unsigned char arg) { farg.kind = '1'; farg.i1 = arg; }
+#if __SIZEOF_WCHAR_T__ == 1
+ inline void assign (FormatArg &farg, wchar_t arg) { farg.kind = '1'; farg.i1 = arg; }
+#endif
+ inline void assign (FormatArg &farg, short arg) { farg.kind = '2'; farg.i2 = arg; }
+ inline void assign (FormatArg &farg, unsigned short arg) { farg.kind = '2'; farg.i2 = arg; }
+#if __SIZEOF_WCHAR_T__ == 2
+ inline void assign (FormatArg &farg, wchar_t arg) { farg.kind = '2'; farg.i2 = arg; }
+#endif
+ inline void assign (FormatArg &farg, int arg) { farg.kind = '4'; farg.i4 = arg; }
+ inline void assign (FormatArg &farg, unsigned int arg) { farg.kind = '4'; farg.i4 = arg; }
+#if __SIZEOF_WCHAR_T__ == 4
+ inline void assign (FormatArg &farg, wchar_t arg) { farg.kind = '4'; farg.i4 = arg; }
+#endif
+ inline void assign (FormatArg &farg, long arg) { farg.kind = '6'; farg.i6 = arg; }
+ inline void assign (FormatArg &farg, unsigned long arg) { farg.kind = '6'; farg.i6 = arg; }
+ inline void assign (FormatArg &farg, long long arg) { farg.kind = '8'; farg.i8 = arg; }
+ inline void assign (FormatArg &farg, unsigned long long arg) { farg.kind = '8'; farg.i8 = arg; }
+ inline void assign (FormatArg &farg, float arg) { farg.kind = 'f'; farg.f = arg; }
+ inline void assign (FormatArg &farg, double arg) { farg.kind = 'f'; farg.f = arg; }
+ inline void assign (FormatArg &farg, long double arg) { farg.kind = 'd'; farg.d = arg; }
+ inline void assign (FormatArg &farg, char *arg) { farg.kind = 's'; farg.s = arg; }
+ inline void assign (FormatArg &farg, const char *arg) { farg.kind = 's'; farg.s = arg; }
+ inline void assign (FormatArg &farg, const std::string &arg) { assign (farg, arg.c_str()); }
+ inline void assign (FormatArg &farg, void *arg) { farg.kind = 'p'; farg.p = arg; }
+ template<class T> inline void assign (FormatArg &farg, T *const &arg) { assign (farg, (void*) arg); }
+ template<class T> typename std::enable_if<std::is_enum<T>::value, void> // eliminated via SFINAE
+ ::type assign (FormatArg &farg, const T &arg) { farg.kind = '8'; farg.i8 = LLong (arg); }
+ template<class T> typename std::enable_if<std::is_class<T>::value, void> // eliminated via SFINAE
+ ::type assign (FormatArg &farg, const T &arg)
+ {
+ std::ostringstream os;
+ os << arg;
+ temporaries_.push_back (os.str());
+ assign (farg, temporaries_[temporaries_.size()-1]);
+ }
+ const FormatArg& format_arg (size_t nth);
+ uint32_t arg_as_width (size_t nth);
+ uint32_t arg_as_precision (size_t nth);
+ LLong arg_as_longlong (size_t nth);
+ LDouble arg_as_ldouble (size_t nth);
+ const char* arg_as_chars (size_t nth);
+ void* arg_as_ptr (size_t nth);
+ struct Directive {
+ char conversion;
+ uint32_t adjust_left : 1, add_sign : 1, use_width : 1, use_precision : 1;
+ uint32_t alternate_form : 1, zero_padding : 1, add_space : 1, locale_grouping : 1;
+ uint32_t field_width, precision, start, end, value_index, width_index, precision_index;
+ Directive() :
+ conversion (0), adjust_left (0), add_sign (0), use_width (0), use_precision (0),
+ alternate_form (0), zero_padding (0), add_space (0), locale_grouping (0),
+ field_width (0), precision (0), start (0), end (0), value_index (0), width_index (0), precision_index
(0)
+ {}
+ };
+ typedef std::function<std::string (const std::string&)> ArgTransform;
+ FormatArg *const fargs_;
+ const size_t nargs_;
+ const int locale_context_;
+ const ArgTransform &arg_transform_;
+ std::vector<std::string> temporaries_;
+ static std::string format_error (const char *err, const char *format, size_t directive);
+ static const char* parse_directive (const char **stringp, size_t *indexp, Directive *dirp);
+ std::string locale_format (size_t last, const char *format);
+ std::string render_format (size_t last, const char *format);
+ std::string render_directive (const Directive &dir);
+ template<class A> std::string render_arg (const Directive &dir, const char *modifier, A arg);
+ template<size_t N> inline std::string
+ intern_format (const char *format)
+ {
+ return locale_format (N, format);
+ }
+ template<size_t N, class A, class ...Args> inline std::string
+ intern_format (const char *format, const A &arg, const Args &...args)
+ {
+ assign (fargs_[N], arg);
+ return intern_format<N+1> (format, args...);
+ }
+ template<size_t N> inline constexpr
+ StringFormatter (const ArgTransform &arg_transform, size_t nargs, FormatArg (&mem)[N], int lc) :
+ fargs_ (mem), nargs_ (nargs), locale_context_ (lc), arg_transform_ (arg_transform) {}
+public:
+ enum LocaleContext {
+ POSIX_LOCALE,
+ CURRENT_LOCALE,
+ };
+ /** Format a string according to an sprintf() @a format string with @a arguments.
+ * Refer to sprintf() for the format string details, this function is designed to
+ * serve as an sprintf() replacement and mimick its behaviour as close as possible.
+ * Supported format directive features are:
+ * - Formatting flags (sign conversion, padding, alignment), i.e. the flags: [-#0+ ']
+ * - Field width and precision specifications.
+ * - Positional arguments for field width, precision and value.
+ * - Length modifiers are tolerated: i.e. any of [hlLjztqZ].
+ * - The conversion specifiers [spmcCdiouXxFfGgEeAa].
+ *
+ * Additionally, arguments can be transformed after conversion by passing a std::string
+ * conversion function as @a arg_transform. This may e.g. be used for XML character
+ * escaping of the format argument values. <br/>
+ * @NOTE Format errors, e.g. missing arguments will produce a warning on stderr and
+ * return the @a format string unmodified.
+ * @returns A formatted string.
+ */
+ template<LocaleContext LC = POSIX_LOCALE, class ...Args>
+ static __attribute__ ((__format__ (printf, 2, 0), noinline)) std::string
+ format (const ArgTransform &arg_transform, const char *format, const Args &...arguments)
+ {
+ constexpr size_t N = sizeof... (Args);
+ FormatArg mem[N ? N : 1];
+ StringFormatter formatter (arg_transform, N, mem, LC);
+ return formatter.intern_format<0> (format, arguments...);
+ }
+};
+
+} // Lib
+} // Bse
+
+#endif // __BSE_FORMATTER_HH__
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]