From 225d4f7032076548c73455fb2fd9c7d5cd030f16 Mon Sep 17 00:00:00 2001 From: Gregory Czajkowski Date: Mon, 18 Nov 2013 22:58:39 -0800 Subject: [PATCH 1/3] add support for unsigned long long Want to be able to format the following without any casting std::cout << fmt::str(fmt::Format("{:x}") << 0xAEull) << std::endl; std::cout << fmt::str(fmt::Format("{:x}") << 0xAEul) << std::endl; std::cout << fmt::str(fmt::Format("{:x}") << uint64_t(0xae)) << std::endl; --- format.cc | 12 +++++++++--- format.h | 11 +++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/format.cc b/format.cc index f58ef618..7eccd4f0 100644 --- a/format.cc +++ b/format.cc @@ -400,7 +400,7 @@ void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { ReportError(s, Format("format specifier '{0}' requires numeric argument") << *s); } - if (arg.type == UINT || arg.type == ULONG) { + if (arg.type == UINT || arg.type == ULONG || arg.type == ULLONG) { ReportError(s, Format("format specifier '{0}' requires signed argument") << *s); } @@ -520,7 +520,7 @@ void fmt::BasicFormatter::DoFormat() { ++s; ++num_open_braces_; const Arg &precision_arg = ParseArgIndex(s); - unsigned long value = 0; + unsigned long long value = 0; switch (precision_arg.type) { case INT: if (precision_arg.int_value < 0) @@ -538,12 +538,15 @@ void fmt::BasicFormatter::DoFormat() { case ULONG: value = precision_arg.ulong_value; break; + case ULLONG: + value = precision_arg.ulong_long_value; + break; default: ReportError(s, "precision is not integer"); } if (value > INT_MAX) ReportError(s, "number is too big in format"); - precision = value; + precision = static_cast(value); if (*s++ != '}') throw FormatError("unmatched '{' in format"); --num_open_braces_; @@ -579,6 +582,9 @@ void fmt::BasicFormatter::DoFormat() { case ULONG: writer.FormatInt(arg.ulong_value, spec); break; + case ULLONG: + writer.FormatInt(arg.ulong_long_value, spec); + break; case DOUBLE: writer.FormatDouble(arg.double_value, spec, precision); break; diff --git a/format.h b/format.h index da010302..2160e4ac 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 {}; @@ -451,6 +454,7 @@ DEFINE_INT_FORMATTERS(int) DEFINE_INT_FORMATTERS(long) DEFINE_INT_FORMATTERS(unsigned) DEFINE_INT_FORMATTERS(unsigned long) +DEFINE_INT_FORMATTERS(unsigned long long) template class BasicFormatter; @@ -819,7 +823,7 @@ class BasicFormatter { enum Type { // Numeric types should go first. - INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE, + INT, UINT, LONG, ULONG, ULLONG, DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, CHAR, STRING, WSTRING, POINTER, CUSTOM }; @@ -854,6 +858,7 @@ class BasicFormatter { double double_value; long long_value; unsigned long ulong_value; + unsigned long long ulong_long_value; long double long_double_value; const void *pointer_value; struct { @@ -873,6 +878,7 @@ class BasicFormatter { Arg(unsigned value) : type(UINT), uint_value(value), formatter(0) {} Arg(long value) : type(LONG), long_value(value), formatter(0) {} Arg(unsigned long value) : type(ULONG), ulong_value(value), formatter(0) {} + Arg(unsigned long long value) : type(ULLONG), ulong_long_value(value), formatter(0) {} Arg(float value) : type(DOUBLE), double_value(value), formatter(0) {} Arg(double value) : type(DOUBLE), double_value(value), formatter(0) {} Arg(long double value) @@ -1151,7 +1157,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; @@ -1186,6 +1192,7 @@ class FormatInt { *--str_ = '-'; } explicit FormatInt(unsigned value) : str_(FormatDecimal(value)) {} + explicit FormatInt(uint64_t value) : str_(FormatDecimal(value)) {} inline const char *c_str() const { return str_; } inline std::string str() const { return str_; } From 62466cc3d9f0820013a64a0a0f51ca9f3837a946 Mon Sep 17 00:00:00 2001 From: Gregory Czajkowski Date: Tue, 19 Nov 2013 20:48:36 -0800 Subject: [PATCH 2/3] add support for better error messages This includes extended error messages which show the original format string. It works for 'char' type with wchar_t returning only the message. The thrown exception class now has a format() method to retrieve the pointer to the original format. --- format.cc | 447 ++++++++++++++++++++++++++++-------------------------- format.h | 10 ++ 2 files changed, 246 insertions(+), 211 deletions(-) diff --git a/format.cc b/format.cc index 7eccd4f0..83100787 100644 --- a/format.cc +++ b/format.cc @@ -409,241 +409,248 @@ void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { template void fmt::BasicFormatter::DoFormat() { - const Char *start = format_; + const Char *start = format_; // capture the format string before it is reset + const Char *original = format_; 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; - 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 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; - case ULLONG: - value = precision_arg.ulong_long_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 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; + case ULLONG: + value = precision_arg.ulong_long_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 ULLONG: + writer.FormatInt(arg.ulong_long_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 = static_cast(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 ULLONG: - writer.FormatInt(arg.ulong_long_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); - } - } 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.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.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; - } + } + } catch (const FormatError &e) { + // rethrow FormatError with the format string pointed to by start + throw BasicFormatError(e.what(), original); } + writer.buffer_.append(start, s); } @@ -679,6 +686,17 @@ template void fmt::BasicFormatter::CheckSign( template void fmt::BasicFormatter::DoFormat(); +const char* FormatErrorMessage(const std::string &message, const char* format) { + return fmt::c_str(fmt::Format("error: {} while parsing {}") << message << format); +} + +template<> fmt::BasicFormatError::BasicFormatError(const std::string &message, const char *format) + : std::runtime_error(FormatErrorMessage(message, format)), format_(format) {} + +template<> fmt::BasicFormatError::~BasicFormatError() { + std::runtime_error::~runtime_error(); +} + // Explicit instantiations for wchar_t. template void fmt::BasicWriter::FormatDouble( @@ -711,3 +729,10 @@ template void fmt::BasicFormatter::CheckSign( const wchar_t *&s, const Arg &arg); template void fmt::BasicFormatter::DoFormat(); + +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 2160e4ac..b5552bb7 100644 --- a/format.h +++ b/format.h @@ -292,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 }; From 5dd604b4813d427b9749b7e7e31b19f623dd31ce Mon Sep 17 00:00:00 2001 From: Gregory Czajkowski Date: Wed, 20 Nov 2013 13:44:10 -0800 Subject: [PATCH 3/3] separated unsigned long long support put the better error message on a separate branch --- format.cc | 465 ++++++++++++++++++++++++++---------------------------- format.h | 14 +- 2 files changed, 230 insertions(+), 249 deletions(-) diff --git a/format.cc b/format.cc index 83100787..7a025387 100644 --- a/format.cc +++ b/format.cc @@ -160,6 +160,12 @@ void fmt::BasicWriter::FormatDecimal( buffer[0] = internal::DIGITS[index]; } +template +void fmt::BasicWriter::FormatDecimal( + CharPtr buffer, unsigned long long value, unsigned num_digits) { + return fmt::BasicWriter::FormatDecimal(buffer, static_cast(value), num_digits); +} + template typename fmt::BasicWriter::CharPtr fmt::BasicWriter::PrepareFilledBuffer( @@ -409,248 +415,241 @@ void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { template void fmt::BasicFormatter::DoFormat() { - const Char *start = format_; // capture the format string before it is reset - const Char *original = format_; + const Char *start = format_; format_ = 0; next_arg_index_ = 0; const Char *s = start; typedef internal::Array::INLINE_BUFFER_SIZE> Buffer; BasicWriter &writer = *writer_; - 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); + 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); + const Arg &arg = ParseArgIndex(s); - FormatSpec spec; - int precision = -1; - if (*s == ':') { - ++s; + FormatSpec spec; + 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); - } - - // 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"); + // 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; - spec.fill_ = '0'; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; } - // 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. + 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); + } + + // 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"); + spec.width_ = value; + } + + // Parse precision. + if (*s == '.') { + ++s; + precision = 0; + if ('0' <= *s && *s <= '9') { unsigned value = ParseUInt(s); if (value > INT_MAX) ReportError(s, "number is too big in format"); - spec.width_ = value; - } - - // Parse precision. - if (*s == '.') { + precision = value; + } else if (*s == '{') { ++s; - 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 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; - case ULLONG: - value = precision_arg.ulong_long_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 ULLONG: - writer.FormatInt(arg.ulong_long_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); + ++num_open_braces_; + const Arg &precision_arg = ParseArgIndex(s); + unsigned long 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; + case ULLONG: + value = precision_arg.ulong_long_value; + break; + default: + ReportError(s, "precision is not integer"); } + 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 { - out = writer.GrowBuffer(1); + ReportError(s, "missing precision in format"); } - *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); + if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { + ReportError(s, + "precision specifier requires floating-point argument"); } - writer.FormatString(str, size, spec); - break; } - 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; + + // 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 ULLONG: + writer.FormatInt(arg.ulong_long_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); + } + } else { + out = writer.GrowBuffer(1); } - } - } catch (const FormatError &e) { - // rethrow FormatError with the format string pointed to by start - throw BasicFormatError(e.what(), original); + *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; + } + 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; + } } - writer.buffer_.append(start, s); } @@ -669,6 +668,9 @@ template fmt::BasicWriter::CharPtr template void fmt::BasicWriter::FormatDecimal( CharPtr buffer, uint64_t value, unsigned num_digits); +template void fmt::BasicWriter::FormatDecimal( + CharPtr buffer, unsigned long long value, unsigned num_digits); + template fmt::BasicWriter::CharPtr fmt::BasicWriter::PrepareFilledBuffer( unsigned size, const AlignSpec &spec, char sign); @@ -686,17 +688,6 @@ template void fmt::BasicFormatter::CheckSign( template void fmt::BasicFormatter::DoFormat(); -const char* FormatErrorMessage(const std::string &message, const char* format) { - return fmt::c_str(fmt::Format("error: {} while parsing {}") << message << format); -} - -template<> fmt::BasicFormatError::BasicFormatError(const std::string &message, const char *format) - : std::runtime_error(FormatErrorMessage(message, format)), format_(format) {} - -template<> fmt::BasicFormatError::~BasicFormatError() { - std::runtime_error::~runtime_error(); -} - // Explicit instantiations for wchar_t. template void fmt::BasicWriter::FormatDouble( @@ -712,6 +703,9 @@ template fmt::BasicWriter::CharPtr template void fmt::BasicWriter::FormatDecimal( CharPtr buffer, uint64_t value, unsigned num_digits); +template void fmt::BasicWriter::FormatDecimal( + CharPtr buffer, unsigned long long value, unsigned num_digits); + template fmt::BasicWriter::CharPtr fmt::BasicWriter::PrepareFilledBuffer( unsigned size, const AlignSpec &spec, char sign); @@ -729,10 +723,3 @@ template void fmt::BasicFormatter::CheckSign( const wchar_t *&s, const Arg &arg); template void fmt::BasicFormatter::DoFormat(); - -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 b5552bb7..21792e5c 100644 --- a/format.h +++ b/format.h @@ -292,16 +292,6 @@ 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 }; @@ -521,6 +511,9 @@ class BasicWriter { static void FormatDecimal( CharPtr buffer, uint64_t value, unsigned num_digits); + static void FormatDecimal( + CharPtr buffer, unsigned long long value, unsigned num_digits); + static CharPtr FillPadding(CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill); @@ -1203,6 +1196,7 @@ class FormatInt { } explicit FormatInt(unsigned value) : str_(FormatDecimal(value)) {} explicit FormatInt(uint64_t value) : str_(FormatDecimal(value)) {} + explicit FormatInt(unsigned long long value) : str_(FormatDecimal(value)) {} inline const char *c_str() const { return str_; } inline std::string str() const { return str_; }