/* Formatting library for C++ Copyright (c) 2012 - 2016, Victor Zverovich All rights reserved. For the license information refer to format.h. */ #ifndef FMT_PRINTF_H_ #define FMT_PRINTF_H_ #include // std::fill_n #include // std::numeric_limits #include "ostream.h" namespace fmt { namespace internal { // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template struct IntChecker { template static bool fits_in_int(T value) { unsigned max = std::numeric_limits::max(); return value <= max; } static bool fits_in_int(bool) { return true; } }; template<> struct IntChecker { template static bool fits_in_int(T value) { return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); } static bool fits_in_int(int) { return true; } }; class PrecisionHandler : public ArgVisitor { public: void report_unhandled_arg() { FMT_THROW(FormatError("precision is not integer")); } template int visit_any_int(T value) { if (!IntChecker::is_signed>::fits_in_int(value)) FMT_THROW(FormatError("number is too big")); return static_cast(value); } }; // IsZeroInt::visit(arg) returns true iff arg is a zero integer. class IsZeroInt : public ArgVisitor { public: template bool visit_any_int(T value) { return value == 0; } }; // returns the default type for format specific "%s" class DefaultType : public ArgVisitor { public: char visit_char(int) { return 'c'; } char visit_bool(bool) { return 's'; } char visit_pointer(const void *) { return 'p'; } template char visit_any_int(T) { return 'd'; } template char visit_any_double(T) { return 'g'; } char visit_unhandled_arg() { return 's'; } }; template struct is_same { enum { value = 0 }; }; template struct is_same { enum { value = 1 }; }; // An argument visitor that converts an integer argument to T for printf, // if T is an integral type. If T is void, the argument is converted to // corresponding signed or unsigned type depending on the type specifier: // 'd' and 'i' - signed, other - unsigned) template class ArgConverter : public ArgVisitor, void> { private: internal::Arg &arg_; wchar_t type_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); public: ArgConverter(internal::Arg &arg, wchar_t type) : arg_(arg) , type_(type) { } void visit_bool(bool value) { if (type_ != 's') visit_any_int(value); } void visit_char(int value) { if (type_ != 's') visit_any_int(value); } template void visit_any_int(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; if (type_ == 's') { is_signed = std::numeric_limits::is_signed; } using internal::Arg; typedef typename internal::Conditional::value, U, T>::type TargetType; if (const_check(sizeof(TargetType) <= sizeof(int))) { // Extra casts are used to silence warnings. if (is_signed) { arg_.type = Arg::INT; arg_.int_value = static_cast(static_cast(value)); } else { arg_.type = Arg::UINT; typedef typename internal::MakeUnsigned::Type Unsigned; arg_.uint_value = static_cast(static_cast(value)); } } else { if (is_signed) { arg_.type = Arg::LONG_LONG; // glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB. arg_.long_long_value = static_cast(value); } else { arg_.type = Arg::ULONG_LONG; arg_.ulong_long_value = static_cast::Type>(value); } } } }; // Converts an integer argument to char for printf. class CharConverter : public ArgVisitor { private: internal::Arg &arg_; FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); public: explicit CharConverter(internal::Arg &arg) : arg_(arg) { } template void visit_any_int(T value) { arg_.type = internal::Arg::CHAR; arg_.int_value = static_cast(value); } }; // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. class WidthHandler : public ArgVisitor { private: FormatSpec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); public: explicit WidthHandler(FormatSpec &spec) : spec_(spec) { } void report_unhandled_arg() { FMT_THROW(FormatError("width is not integer")); } template unsigned visit_any_int(T value) { typedef typename internal::IntTraits::MainType UnsignedType; UnsignedType width = static_cast(value); if (internal::is_negative(value)) { spec_.align_ = ALIGN_LEFT; width = 0 - width; } unsigned int_max = std::numeric_limits::max(); if (width > int_max) FMT_THROW(FormatError("number is too big")); return static_cast(width); } }; } // namespace internal /** \rst A ``printf`` argument formatter based on the `curiously recurring template pattern `_. To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some or all of the visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. Pass the subclass as the *Impl* template parameter. When a formatting function processes an argument, it will dispatch to a visit method specific to the argument type. For example, if the argument type is ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass will be called. If the subclass doesn't contain a method with this signature, then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its superclass will be called. \endrst */ template class BasicPrintfArgFormatter : public internal::ArgFormatterBase { private: void write_null_pointer() { this->spec().type_ = 0; this->write("(nil)"); } typedef internal::ArgFormatterBase Base; public: /** \rst Constructs an argument formatter object. *writer* is a reference to the output writer and *spec* contains format specifier information for standard argument types. \endrst */ BasicPrintfArgFormatter(BasicWriter &w, Spec &s) : internal::ArgFormatterBase(w, s) { } /** Formats an argument of type ``bool``. */ void visit_bool(bool value) { Spec &fmt_spec = this->spec(); if (fmt_spec.type_ != 's') return this->visit_any_int(value); fmt_spec.type_ = 0; this->write(value); } /** Formats a character. */ void visit_char(int value) { const Spec &fmt_spec = this->spec(); BasicWriter &w = this->writer(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') w.write_int(value, fmt_spec); typedef typename BasicWriter::CharPtr CharPtr; CharPtr out = CharPtr(); if (fmt_spec.width_ > 1) { Char fill = ' '; out = w.grow_buffer(fmt_spec.width_); if (fmt_spec.align_ != ALIGN_LEFT) { std::fill_n(out, fmt_spec.width_ - 1, fill); out += fmt_spec.width_ - 1; } else { std::fill_n(out + 1, fmt_spec.width_ - 1, fill); } } else { out = w.grow_buffer(1); } *out = static_cast(value); } /** Formats a null-terminated C string. */ void visit_cstring(const char *value) { if (value) Base::visit_cstring(value); else if (this->spec().type_ == 'p') write_null_pointer(); else this->write("(null)"); } /** Formats a pointer. */ void visit_pointer(const void *value) { if (value) return Base::visit_pointer(value); this->spec().type_ = 0; write_null_pointer(); } /** Formats an argument of a custom (user-defined) type. */ void visit_custom(internal::Arg::CustomValue c) { BasicFormatter formatter(ArgList(), this->writer()); const Char format_str[] = {'}', 0}; const Char *format = format_str; c.format(&formatter, c.value, &format); } }; /** The default printf argument formatter. */ template class PrintfArgFormatter : public BasicPrintfArgFormatter, Char, FormatSpec> { public: /** Constructs an argument formatter object. */ PrintfArgFormatter(BasicWriter &w, FormatSpec &s) : BasicPrintfArgFormatter, Char, FormatSpec>(w, s) { } }; /** This template formats data and writes the output to a writer. */ template> class PrintfFormatter : private internal::FormatterBase { private: BasicWriter &writer_; void parse_flags(FormatSpec &spec, const Char *&s); // Returns the argument with specified index or, if arg_index is equal // to the maximum unsigned value, the next argument. internal::Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits::max)()); // Parses argument index, flags and width and returns the argument index. unsigned parse_header(const Char *&s, FormatSpec &spec); public: /** \rst Constructs a ``PrintfFormatter`` object. References to the arguments and the writer are stored in the formatter object so make sure they have appropriate lifetimes. \endrst */ explicit PrintfFormatter(const ArgList &al, BasicWriter &w) : FormatterBase(al) , writer_(w) { } /** Formats stored arguments and writes the output to the writer. */ void format(BasicCStringRef format_str); }; template void PrintfFormatter::parse_flags(FormatSpec &spec, const Char *&s) { for (;;) { switch (*s++) { case '-': spec.align_ = ALIGN_LEFT; break; case '+': spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '0': spec.fill_ = '0'; break; case ' ': spec.flags_ |= SIGN_FLAG; break; case '#': spec.flags_ |= HASH_FLAG; break; default: --s; return; } } } template internal::Arg PrintfFormatter::get_arg(const Char *s, unsigned arg_index) { (void)s; const char *error = FMT_NULL; internal::Arg arg = arg_index == std::numeric_limits::max() ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); if (error) FMT_THROW(FormatError(!*s ? "invalid format string" : error)); return arg; } template unsigned PrintfFormatter::parse_header(const Char *&s, FormatSpec &spec) { unsigned arg_index = std::numeric_limits::max(); Char c = *s; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). unsigned value = internal::parse_nonnegative_int(s); if (*s == '$') // value is an argument index { ++s; arg_index = value; } else { if (c == '0') spec.fill_ = '0'; if (value != 0) { // Nonzero value means that we parsed width and don't need to // parse it or flags again, so return now. spec.width_ = value; return arg_index; } } } parse_flags(spec, s); // Parse width. if (*s >= '0' && *s <= '9') { spec.width_ = internal::parse_nonnegative_int(s); } else if (*s == '*') { ++s; spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); } return arg_index; } template void PrintfFormatter::format(BasicCStringRef format_str) { const Char *start = format_str.c_str(); const Char *s = start; while (*s) { Char c = *s++; if (c != '%') continue; if (*s == c) { write(writer_, start, s); start = ++s; continue; } write(writer_, start, s - 1); FormatSpec spec; spec.align_ = ALIGN_RIGHT; // Parse argument index, flags and width. unsigned arg_index = parse_header(s, spec); // Parse precision. if (*s == '.') { ++s; if ('0' <= *s && *s <= '9') { spec.precision_ = static_cast(internal::parse_nonnegative_int(s)); } else if (*s == '*') { ++s; spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); } else { spec.precision_ = 0; } } using internal::Arg; Arg arg = get_arg(s, arg_index); if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg)) spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { if (arg.type <= Arg::LAST_NUMERIC_TYPE) spec.align_ = ALIGN_NUMERIC; else spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. } // Parse length and convert the argument to the required type. using internal::ArgConverter; switch (*s++) { case 'h': if (*s == 'h') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'l': if (*s == 'l') ArgConverter(arg, *++s).visit(arg); else ArgConverter(arg, *s).visit(arg); break; case 'j': ArgConverter(arg, *s).visit(arg); break; case 'z': ArgConverter(arg, *s).visit(arg); break; case 't': ArgConverter(arg, *s).visit(arg); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; default: --s; ArgConverter(arg, *s).visit(arg); } // Parse type. if (!*s) FMT_THROW(FormatError("invalid format string")); spec.type_ = static_cast(*s++); if (spec.type_ == 's') { // set the format type to the default if 's' is specified spec.type_ = internal::DefaultType().visit(arg); } if (arg.type <= Arg::LAST_INTEGER_TYPE) { // Normalize type. switch (spec.type_) { case 'i': case 'u': spec.type_ = 'd'; break; case 'c': // TODO: handle wchar_t internal::CharConverter(arg).visit(arg); break; } } start = s; // Format argument. AF(writer_, spec).visit(arg); } write(writer_, start, s); } inline void printf(Writer &w, CStringRef format, ArgList args) { PrintfFormatter(args, w).format(format); } FMT_VARIADIC(void, printf, Writer &, CStringRef) inline void printf(WWriter &w, WCStringRef format, ArgList args) { PrintfFormatter(args, w).format(format); } FMT_VARIADIC(void, printf, WWriter &, WCStringRef) /** \rst Formats arguments and returns the result as a string. **Example**:: std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ inline std::string sprintf(CStringRef format, ArgList args) { MemoryWriter w; printf(w, format, args); return w.str(); } FMT_VARIADIC(std::string, sprintf, CStringRef) inline std::wstring sprintf(WCStringRef format, ArgList args) { WMemoryWriter w; printf(w, format, args); return w.str(); } FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) /** \rst Prints formatted data to the file *f*. **Example**:: fmt::fprintf(stderr, "Don't %s!", "panic"); \endrst */ FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) /** \rst Prints formatted data to ``stdout``. **Example**:: fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ inline int printf(CStringRef format, ArgList args) { return fprintf(stdout, format, args); } FMT_VARIADIC(int, printf, CStringRef) /** \rst Prints formatted data to the stream *os*. **Example**:: fprintf(cerr, "Don't %s!", "panic"); \endrst */ inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) { MemoryWriter w; printf(w, format_str, args); internal::write(os, w); return static_cast(w.size()); } FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) } // namespace fmt #ifdef FMT_HEADER_ONLY #include "printf.cc" #endif #endif // FMT_PRINTF_H_