diff --git a/format.cc b/format.cc index f6689e54..2ffe3738 100644 --- a/format.cc +++ b/format.cc @@ -110,15 +110,14 @@ const char fmt::internal::DIGITS[] = "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; -template -void fmt::internal::ReportUnknownType(const Char *format, char code, const char *type) { +void fmt::internal::ReportUnknownType(char code, const char *type) { if (std::isprint(static_cast(code))) { throw fmt::FormatError(fmt::str( - fmt::Format("unknown format code '{}' for {} while parsing {}") << code << type << format)); + fmt::Format("unknown format code '{}' for {}") << code << type)); } throw fmt::FormatError( - fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {} while parsing {}") - << static_cast(code) << type << format)); + fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {}") + << static_cast(code) << type)); } @@ -201,7 +200,7 @@ typename fmt::BasicWriter::CharPtr template template void fmt::BasicWriter::FormatDouble( - T value, const FormatSpec &spec, int precision) { + T value, const FormatSpec &spec, int precision) { // Check type. char type = spec.type(); bool upper = false; @@ -221,7 +220,7 @@ void fmt::BasicWriter::FormatDouble( upper = true; break; default: - internal::ReportUnknownType(spec.format(), type, "double"); + internal::ReportUnknownType(type, "double"); break; } @@ -411,247 +410,251 @@ void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { template void fmt::BasicFormatter::DoFormat() { const Char *start = format_; + const Char *original = format_; // capture the format string before it is reset format_ = 0; next_arg_index_ = 0; const Char *s = start; typedef internal::Array::INLINE_BUFFER_SIZE> Buffer; BasicWriter &writer = *writer_; - while (*s) { - Char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) { - writer.buffer_.append(start, s); - start = ++s; - continue; - } - if (c == '}') - throw FormatError("unmatched '}' in format"); - num_open_braces_= 1; - writer.buffer_.append(start, s - 1); - - const Arg &arg = ParseArgIndex(s); - - FormatSpec spec(format_); - int precision = -1; - if (*s == ':') { - ++s; - - // Parse fill and alignment. - if (Char c = *s) { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do { - switch (*p) { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { - if (c == '}') break; - if (c == '{') - ReportError(s, "invalid fill character '{'"); - s += 2; - spec.fill_ = c; - } else ++s; - if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '=' requires numeric argument"); - break; - } - } while (--p >= s); + try { + while (*s) { + Char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + writer.buffer_.append(start, s); + start = ++s; + continue; } + if (c == '}') + throw FormatError("unmatched '}' in format"); + num_open_braces_= 1; + writer.buffer_.append(start, s - 1); - // Parse sign. - switch (*s) { - case '+': - CheckSign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - CheckSign(s, arg); - break; - case ' ': - CheckSign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } + const Arg &arg = ParseArgIndex(s); - if (*s == '#') { - if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '#' requires numeric argument"); - spec.flags_ |= HASH_FLAG; + FormatSpec spec; + int precision = -1; + if (*s == ':') { ++s; - } - // Parse width and zero flag. - if ('0' <= *s && *s <= '9') { - if (*s == '0') { - if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '0' requires numeric argument"); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; + // Parse fill and alignment. + if (Char c = *s) { + const Char *p = s + 1; + spec.align_ = ALIGN_DEFAULT; + do { + switch (*p) { + case '<': + spec.align_ = ALIGN_LEFT; + break; + case '>': + spec.align_ = ALIGN_RIGHT; + break; + case '=': + spec.align_ = ALIGN_NUMERIC; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; + } + if (spec.align_ != ALIGN_DEFAULT) { + if (p != s) { + if (c == '}') break; + if (c == '{') + ReportError(s, "invalid fill character '{'"); + s += 2; + spec.fill_ = c; + } else ++s; + if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '=' requires numeric argument"); + break; + } + } while (--p >= s); } - // Zero may be parsed again as a part of the width, but it is simpler - // and more efficient than checking if the next char is a digit. - unsigned value = ParseUInt(s); - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - spec.width_ = value; - } - // Parse precision. - if (*s == '.') { - ++s; - precision = 0; + // Parse sign. + switch (*s) { + case '+': + CheckSign(s, arg); + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '-': + CheckSign(s, arg); + break; + case ' ': + CheckSign(s, arg); + spec.flags_ |= SIGN_FLAG; + break; + } + + if (*s == '#') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '#' requires numeric argument"); + spec.flags_ |= HASH_FLAG; + ++s; + } + + // Parse width and zero flag. if ('0' <= *s && *s <= '9') { + if (*s == '0') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '0' requires numeric argument"); + spec.align_ = ALIGN_NUMERIC; + spec.fill_ = '0'; + } + // Zero may be parsed again as a part of the width, but it is simpler + // and more efficient than checking if the next char is a digit. unsigned value = ParseUInt(s); if (value > INT_MAX) ReportError(s, "number is too big in format"); - precision = value; - } else if (*s == '{') { + spec.width_ = value; + } + + // Parse precision. + if (*s == '.') { ++s; - ++num_open_braces_; - const Arg &precision_arg = ParseArgIndex(s); - unsigned long value = 0; - switch (precision_arg.type) { - case INT: - if (precision_arg.int_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.int_value; - break; - case UINT: - value = precision_arg.uint_value; - break; - case LONG: - if (precision_arg.long_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.long_value; - break; - case ULONG: - value = precision_arg.ulong_value; - break; - default: - ReportError(s, "precision is not integer"); + precision = 0; + if ('0' <= *s && *s <= '9') { + unsigned value = ParseUInt(s); + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + precision = value; + } else if (*s == '{') { + ++s; + ++num_open_braces_; + const Arg &precision_arg = ParseArgIndex(s); + unsigned long value = 0; + switch (precision_arg.type) { + case INT: + if (precision_arg.int_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.int_value; + break; + case UINT: + value = precision_arg.uint_value; + break; + case LONG: + if (precision_arg.long_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.long_value; + break; + case ULONG: + value = precision_arg.ulong_value; + break; + default: + ReportError(s, "precision is not integer"); + } + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + precision = static_cast(value); + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + --num_open_braces_; + } else { + ReportError(s, "missing precision in format"); + } + if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { + ReportError(s, + "precision specifier requires floating-point argument"); + } + } + + // Parse type. + if (*s != '}' && *s) + spec.type_ = static_cast(*s++); + } + + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + start = s; + + // Format argument. + switch (arg.type) { + case INT: + writer.FormatInt(arg.int_value, spec); + break; + case UINT: + writer.FormatInt(arg.uint_value, spec); + break; + case LONG: + writer.FormatInt(arg.long_value, spec); + break; + case ULONG: + writer.FormatInt(arg.ulong_value, spec); + break; + case DOUBLE: + writer.FormatDouble(arg.double_value, spec, precision); + break; + case LONG_DOUBLE: + writer.FormatDouble(arg.long_double_value, spec, precision); + break; + case CHAR: { + if (spec.type_ && spec.type_ != 'c') + internal::ReportUnknownType(spec.type_, "char"); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec.width_ > 1) { + Char fill = static_cast(spec.fill()); + out = writer.GrowBuffer(spec.width_); + if (spec.align_ == ALIGN_RIGHT) { + std::fill_n(out, spec.width_ - 1, fill); + out += spec.width_ - 1; + } else if (spec.align_ == ALIGN_CENTER) { + out = writer.FillPadding(out, spec.width_, 1, fill); + } else { + std::fill_n(out + 1, spec.width_ - 1, fill); } - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - precision = value; - if (*s++ != '}') - throw FormatError("unmatched '{' in format"); - --num_open_braces_; } else { - ReportError(s, "missing precision in format"); + out = writer.GrowBuffer(1); } - if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { - ReportError(s, - "precision specifier requires floating-point argument"); + *out = arg.int_value; + break; + } + case STRING: { + if (spec.type_ && spec.type_ != 's') + internal::ReportUnknownType(spec.type_, "string"); + const Char *str = arg.string.value; + std::size_t size = arg.string.size; + if (size == 0) { + if (!str) + throw FormatError("string pointer is null"); + if (*str) + size = std::char_traits::length(str); } + writer.FormatString(str, size, spec); + break; } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } - - if (*s++ != '}') - throw FormatError("unmatched '{' in format"); - start = s; - - // Format argument. - switch (arg.type) { - case INT: - writer.FormatInt(arg.int_value, spec); - break; - case UINT: - writer.FormatInt(arg.uint_value, spec); - break; - case LONG: - writer.FormatInt(arg.long_value, spec); - break; - case ULONG: - writer.FormatInt(arg.ulong_value, spec); - break; - case DOUBLE: - writer.FormatDouble(arg.double_value, spec, precision); - break; - case LONG_DOUBLE: - writer.FormatDouble(arg.long_double_value, spec, precision); - break; - case CHAR: { - if (spec.type_ && spec.type_ != 'c') - internal::ReportUnknownType(spec.format_, spec.type_, "char"); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - Char fill = static_cast(spec.fill()); - out = writer.GrowBuffer(spec.width_); - if (spec.align_ == ALIGN_RIGHT) { - std::fill_n(out, spec.width_ - 1, fill); - out += spec.width_ - 1; - } else if (spec.align_ == ALIGN_CENTER) { - out = writer.FillPadding(out, spec.width_, 1, fill); - } else { - std::fill_n(out + 1, spec.width_ - 1, fill); - } - } else { - out = writer.GrowBuffer(1); + case POINTER: + if (spec.type_ && spec.type_ != 'p') + internal::ReportUnknownType(spec.type_, "pointer"); + spec.flags_= HASH_FLAG; + spec.type_ = 'x'; + writer.FormatInt(reinterpret_cast(arg.pointer_value), spec); + break; + case CUSTOM: + if (spec.type_) + internal::ReportUnknownType(spec.type_, "object"); + arg.custom.format(writer, arg.custom.value, spec); + break; + default: + assert(false); + break; } - *out = arg.int_value; - break; - } - case STRING: { - if (spec.type_ && spec.type_ != 's') - internal::ReportUnknownType(spec.format_, spec.type_, "string"); - const Char *str = arg.string.value; - std::size_t size = arg.string.size; - if (size == 0) { - if (!str) - throw FormatError("string pointer is null"); - if (*str) - size = std::char_traits::length(str); - } - writer.FormatString(str, size, spec); - break; - } - case POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::ReportUnknownType(spec.format_, spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer.FormatInt(reinterpret_cast(arg.pointer_value), spec); - break; - case CUSTOM: - if (spec.type_) - internal::ReportUnknownType(spec.format_, spec.type_, "object"); - arg.custom.format(writer, arg.custom.value, spec); - break; - default: - assert(false); - break; - } + } + } catch (const FormatError &e) { + // rethrow FormatError with the format string pointed to by start + throw BasicFormatError(e.what(), original); } + writer.buffer_.append(start, s); } // Explicit instantiations for char. -template void fmt::internal::ReportUnknownType( - const char *format, char code, const char *type); - template void fmt::BasicWriter::FormatDouble( - double value, const FormatSpec &spec, int precision); + double value, const FormatSpec &spec, int precision); template void fmt::BasicWriter::FormatDouble( - long double value, const FormatSpec &spec, int precision); + long double value, const FormatSpec &spec, int precision); template fmt::BasicWriter::CharPtr fmt::BasicWriter::FillPadding(CharPtr buffer, @@ -677,16 +680,20 @@ template void fmt::BasicFormatter::CheckSign( template void fmt::BasicFormatter::DoFormat(); +template<> fmt::BasicFormatError::BasicFormatError(const std::string &message, const char *format) + : std::runtime_error(message), format_(format) {} + +template<> fmt::BasicFormatError::~BasicFormatError() { + std::runtime_error::~runtime_error(); +} + // Explicit instantiations for wchar_t. -template void fmt::internal::ReportUnknownType( - const wchar_t *format, char code, const char *type); - template void fmt::BasicWriter::FormatDouble( - double value, const FormatSpec &spec, int precision); + double value, const FormatSpec &spec, int precision); template void fmt::BasicWriter::FormatDouble( - long double value, const FormatSpec &spec, int precision); + long double value, const FormatSpec &spec, int precision); template fmt::BasicWriter::CharPtr fmt::BasicWriter::FillPadding(CharPtr buffer, @@ -713,4 +720,9 @@ template void fmt::BasicFormatter::CheckSign( template void fmt::BasicFormatter::DoFormat(); -//template fmt::BasicFormatter::Arg::Arg(wchar_t const*); +template<> fmt::BasicFormatError::BasicFormatError(const std::string &message, const wchar_t *format) + : std::runtime_error(message), format_(format){} + +template<> fmt::BasicFormatError::~BasicFormatError() { + std::runtime_error::~runtime_error(); +} diff --git a/format.h b/format.h index 507e6dfe..22833a9b 100644 --- a/format.h +++ b/format.h @@ -189,6 +189,9 @@ struct SignedIntTraits { template <> struct IntTraits : SignedIntTraits {}; +template <> +struct IntTraits : SignedIntTraits {}; + template <> struct IntTraits : SignedIntTraits {}; @@ -201,8 +204,7 @@ struct IsLongDouble { enum {VALUE = 0}; }; template <> struct IsLongDouble { enum {VALUE = 1}; }; -template -void ReportUnknownType(const Char *format, char code, const char *type); +void ReportUnknownType(char code, const char *type); // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case CountDigits returns 1. @@ -290,6 +292,16 @@ class FormatError : public std::runtime_error { : std::runtime_error(message) {} }; +template +class BasicFormatError : public std::runtime_error { +private: + std::basic_string format_; +public: + explicit BasicFormatError(const std::string &message, const Char *format); + virtual ~BasicFormatError() throw(); + const Char *format() const { return format_.c_str(); } +}; + enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC }; @@ -344,14 +356,12 @@ struct AlignTypeSpec : AlignSpec { char type() const { return TYPE; } }; -template struct FormatSpec : AlignSpec { unsigned flags_; char type_; - const Char *format_; - FormatSpec(const Char *format, unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), type_(type), format_(format) {} + FormatSpec(unsigned width = 0, char type = 0, wchar_t fill = ' ') + : AlignSpec(width, fill), flags_(0), type_(type) {} Alignment align() const { return align_; } @@ -360,7 +370,6 @@ struct FormatSpec : AlignSpec { bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; } char type() const { return type_; } - const Char* format() const { return format_; } }; template @@ -532,17 +541,17 @@ class BasicWriter { // Formats an integer. template - void FormatInt(T value, const FormatSpec &spec) { - *this << IntFormatter >(value, spec); + void FormatInt(T value, const FormatSpec &spec) { + *this << IntFormatter(value, spec); } // Formats a floating-point number (double or long double). template - void FormatDouble(T value, const FormatSpec &spec, int precision); + void FormatDouble(T value, const FormatSpec &spec, int precision); template CharPtr FormatString(const StringChar *s, - std::size_t size, const FormatSpec &spec); + std::size_t size, const FormatSpec &spec); // This method is private to disallow writing a wide string to a // char stream and vice versa. If you want to print a wide string @@ -632,7 +641,7 @@ class BasicWriter { } BasicWriter &operator<<(double value) { - FormatDouble(value, FormatSpec(""), -1); + FormatDouble(value, FormatSpec(), -1); return *this; } @@ -641,7 +650,7 @@ class BasicWriter { (``'g'``) and writes it to the stream. */ BasicWriter &operator<<(long double value) { - FormatDouble(value, FormatSpec(""), -1); + FormatDouble(value, FormatSpec(), -1); return *this; } @@ -663,7 +672,7 @@ class BasicWriter { template BasicWriter &operator<<(const IntFormatter &f); - void Write(const std::basic_string &s, const FormatSpec &spec) { + void Write(const std::basic_string &s, const FormatSpec &spec) { FormatString(s.data(), s.size(), spec); } @@ -675,7 +684,7 @@ class BasicWriter { template template typename BasicWriter::CharPtr BasicWriter::FormatString( - const StringChar *s, std::size_t size, const FormatSpec &spec) { + const StringChar *s, std::size_t size, const FormatSpec &spec) { CharPtr out = CharPtr(); if (spec.width() > size) { out = GrowBuffer(spec.width()); @@ -776,7 +785,7 @@ BasicWriter &BasicWriter::operator<<( break; } default: - internal::ReportUnknownType(f.format(), f.type(), "integer"); + internal::ReportUnknownType(f.type(), "integer"); break; } return *this; @@ -792,7 +801,7 @@ typedef BasicWriter WWriter; // The default formatting function. template -void Format(BasicWriter &w, const FormatSpec &spec, const T &value) { +void Format(BasicWriter &w, const FormatSpec &spec, const T &value) { std::basic_ostringstream os; os << value; w.Write(os.str(), spec); @@ -802,7 +811,7 @@ namespace internal { // Formats an argument of a custom type, such as a user-defined class. template void FormatCustomArg( - BasicWriter &w, const void *arg, const FormatSpec &spec) { + BasicWriter &w, const void *arg, const FormatSpec &spec) { Format(w, spec, *static_cast(arg)); } } @@ -829,7 +838,7 @@ class BasicFormatter { }; typedef void (*FormatFunc)( - BasicWriter &w, const void *arg, const FormatSpec &spec); + BasicWriter &w, const void *arg, const FormatSpec &spec); // A format argument. class Arg { @@ -1155,7 +1164,7 @@ class FormatInt { enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; char buffer_[BUFFER_SIZE]; char *str_; - + // Formats value in reverse and returns the number of digits. char *FormatDecimal(uint64_t value) { char *buffer_end = buffer_ + BUFFER_SIZE;