From 225d4f7032076548c73455fb2fd9c7d5cd030f16 Mon Sep 17 00:00:00 2001 From: Gregory Czajkowski Date: Mon, 18 Nov 2013 22:58:39 -0800 Subject: [PATCH 1/5] 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 f58ef6187eca..7eccd4f0ff29 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 da01030218a4..2160e4acc6f7 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/5] 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 | 439 +++++++++++++++++++++++++++++------------------------- format.h | 10 ++ 2 files changed, 242 insertions(+), 207 deletions(-) diff --git a/format.cc b/format.cc index 7eccd4f0ff29..831007876601 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') { + // 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 '0' requires numeric argument"); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; + ReportError(s, "format specifier '#' requires numeric argument"); + spec.flags_ |= HASH_FLAG; + ++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 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"); } - 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++); } - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + start = 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); + // 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 { - std::fill_n(out + 1, spec.width_ - 1, fill); + out = writer.GrowBuffer(1); } - } else { - out = writer.GrowBuffer(1); + *out = arg.int_value; + 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); + 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; } - 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; - } + 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 2160e4acc6f7..b5552bb74b44 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/5] separated unsigned long long support put the better error message on a separate branch --- format.cc | 451 ++++++++++++++++++++++++++---------------------------- format.h | 14 +- 2 files changed, 223 insertions(+), 242 deletions(-) diff --git a/format.cc b/format.cc index 831007876601..7a025387f966 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; + 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); } - if (c == '}') - throw FormatError("unmatched '}' in format"); - num_open_braces_= 1; - writer.buffer_.append(start, s - 1); - const Arg &arg = ParseArgIndex(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; + } - FormatSpec spec; - int precision = -1; - if (*s == ':') { + if (*s == '#') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '#' requires numeric argument"); + spec.flags_ |= HASH_FLAG; ++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 == '#') { + // Parse width and zero flag. + if ('0' <= *s && *s <= '9') { + if (*s == '0') { if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '#' requires numeric argument"); - spec.flags_ |= HASH_FLAG; - ++s; + 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 width and zero flag. + // Parse precision. + if (*s == '.') { + ++s; + precision = 0; 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 == '.') { + 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"); + ++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 { + 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; + // Parse type. + if (*s != '}' && *s) + spec.type_ = static_cast(*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 (*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 { - out = writer.GrowBuffer(1); + std::fill_n(out + 1, spec.width_ - 1, fill); } - *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; + } 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); } - } - } catch (const FormatError &e) { - // rethrow FormatError with the format string pointed to by start - throw BasicFormatError(e.what(), original); + 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 b5552bb74b44..21792e5ccdb3 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_; } From f2e75f74cc7f00bd05e96e5eabec2988a234609f Mon Sep 17 00:00:00 2001 From: Gregory Czajkowski Date: Tue, 10 Dec 2013 17:43:21 -0800 Subject: [PATCH 4/5] merge with vitaut/format --- format.cc | 40 ++++++------ format.h | 180 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 120 insertions(+), 100 deletions(-) diff --git a/format.cc b/format.cc index 7a025387f966..b273dbd2b6a0 100644 --- a/format.cc +++ b/format.cc @@ -160,12 +160,6 @@ 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( @@ -402,13 +396,14 @@ inline const typename fmt::BasicFormatter::Arg template void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { + char sign = static_cast(*s); if (arg.type > LAST_NUMERIC_TYPE) { ReportError(s, - Format("format specifier '{0}' requires numeric argument") << *s); + Format("format specifier '{}' requires numeric argument") << sign); } - if (arg.type == UINT || arg.type == ULONG || arg.type == ULLONG) { + if (arg.type == UINT || arg.type == ULONG || arg.type == ULONG_LONG) { ReportError(s, - Format("format specifier '{0}' requires signed argument") << *s); + Format("format specifier '{}' requires signed argument") << sign); } ++s; } @@ -419,7 +414,6 @@ void fmt::BasicFormatter::DoFormat() { 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++; @@ -526,7 +520,7 @@ void fmt::BasicFormatter::DoFormat() { ++s; ++num_open_braces_; const Arg &precision_arg = ParseArgIndex(s); - unsigned long long value = 0; + ULongLong value = 0; switch (precision_arg.type) { case INT: if (precision_arg.int_value < 0) @@ -544,15 +538,20 @@ void fmt::BasicFormatter::DoFormat() { case ULONG: value = precision_arg.ulong_value; break; - case ULLONG: + case LONG_LONG: + if (precision_arg.long_long_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.long_long_value; + break; + case ULONG_LONG: value = precision_arg.ulong_long_value; - break; + 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_; @@ -588,9 +587,12 @@ void fmt::BasicFormatter::DoFormat() { case ULONG: writer.FormatInt(arg.ulong_value, spec); break; - case ULLONG: + case LONG_LONG: + writer.FormatInt(arg.long_long_value, spec); + break; + case ULONG_LONG: writer.FormatInt(arg.ulong_long_value, spec); - break; + break; case DOUBLE: writer.FormatDouble(arg.double_value, spec, precision); break; @@ -668,9 +670,6 @@ 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); @@ -703,9 +702,6 @@ 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); diff --git a/format.h b/format.h index 21792e5ccdb3..2ac0bdf258b0 100644 --- a/format.h +++ b/format.h @@ -42,24 +42,42 @@ #include #include +#ifdef __GNUC__ +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#endif + // Compatibility with compilers other than clang. #ifndef __has_feature # define __has_feature(x) 0 #endif +#ifndef FMT_USE_INITIALIZER_LIST +# define FMT_USE_INITIALIZER_LIST \ + (__has_feature(cxx_generalized_initializers) || \ + (FMT_GCC_VERSION >= 404 && __cplusplus >= 201103) || _MSC_VER >= 1700) +#endif + +#if FMT_USE_INITIALIZER_LIST +# include +#endif + // Define FMT_USE_NOEXCEPT to make format use noexcept (C++11 feature). -#if FMT_USE_NOEXCEPT || \ - (defined(__has_feature) && __has_feature(cxx_noexcept)) +#if FMT_USE_NOEXCEPT || __has_feature(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && __cplusplus >= 201103) # define FMT_NOEXCEPT(expr) noexcept(expr) #else # define FMT_NOEXCEPT(expr) #endif -#ifdef __GNUC__ -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#if FMT_GCC_VERSION >= 406 +# define FMT_GCC_DIAGNOSTIC # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wlong-long" #endif + +#if _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4521) #endif namespace fmt { @@ -151,11 +169,16 @@ void Array::append(const T *begin, const T *end) { } template -struct CharTraits; +class CharTraits; template <> -struct CharTraits { - typedef wchar_t UnsupportedType; +class CharTraits { + private: + // Conversion from wchar_t to char is not supported. + static char ConvertWChar(wchar_t); + + public: + typedef const wchar_t *UnsupportedStrType; template static int FormatFloat(char *buffer, std::size_t size, @@ -163,8 +186,11 @@ struct CharTraits { }; template <> -struct CharTraits { - typedef char UnsupportedType; +class CharTraits { + public: + typedef const char *UnsupportedStrType; + + static wchar_t ConvertWChar(wchar_t value) { return value; } template static int FormatFloat(wchar_t *buffer, std::size_t size, @@ -189,9 +215,6 @@ struct SignedIntTraits { template <> struct IntTraits : SignedIntTraits {}; -template <> -struct IntTraits : SignedIntTraits {}; - template <> struct IntTraits : SignedIntTraits {}; @@ -379,11 +402,6 @@ class IntFormatter : public SpecT { */ IntFormatter > bin(int value); -/** - Returns an integer formatter that formats the value in base 2. - */ -IntFormatter > binu(int value); - /** Returns an integer formatter that formats the value in base 8. */ @@ -421,9 +439,7 @@ IntFormatter > pad( inline IntFormatter > bin(TYPE value) { \ return IntFormatter >(value, TypeSpec<'b'>()); \ } \ -inline IntFormatter > binu(TYPE value) { \ - return IntFormatter >(value, TypeSpec<'B'>()); \ -} \ + \ inline IntFormatter > oct(TYPE value) { \ return IntFormatter >(value, TypeSpec<'o'>()); \ } \ @@ -454,6 +470,7 @@ DEFINE_INT_FORMATTERS(int) DEFINE_INT_FORMATTERS(long) DEFINE_INT_FORMATTERS(unsigned) DEFINE_INT_FORMATTERS(unsigned long) +DEFINE_INT_FORMATTERS(long long) DEFINE_INT_FORMATTERS(unsigned long long) template @@ -511,9 +528,6 @@ 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); @@ -551,7 +565,7 @@ class BasicWriter { // char stream and vice versa. If you want to print a wide string // as a pointer as std::ostream does, cast it to const void*. // Do not implement! - void operator<<(const typename internal::CharTraits::UnsupportedType *); + void operator<<(typename internal::CharTraits::UnsupportedStrType); public: /** @@ -752,9 +766,8 @@ BasicWriter &BasicWriter::operator<<( } while ((n >>= 1) != 0); Char *p = GetBase(PrepareFilledBuffer(size, f, sign)); n = abs_value; - const char *digits = "01"; do { - *p-- = digits[n & 0x1]; + *p-- = '0' + (n & 1); } while ((n >>= 1) != 0); if (print_prefix) { *p-- = f.type(); @@ -787,7 +800,8 @@ BasicWriter &BasicWriter::operator<<( template BasicFormatter BasicWriter::Format(StringRef format) { - return BasicFormatter(*this, format.c_str()); + BasicFormatter f(*this, format.c_str()); + return f; } typedef BasicWriter Writer; @@ -826,7 +840,7 @@ class BasicFormatter { enum Type { // Numeric types should go first. - INT, UINT, LONG, ULONG, ULLONG, DOUBLE, LONG_DOUBLE, + INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, CHAR, STRING, WSTRING, POINTER, CUSTOM }; @@ -847,12 +861,6 @@ class BasicFormatter { template Arg(T *value); - // This method is private to disallow formatting of wide characters. - // If you want to output a wide character cast it to integer type. - // Do not implement! - // TODO - //Arg(wchar_t value); - public: Type type; union { @@ -861,6 +869,7 @@ class BasicFormatter { double double_value; long long_value; unsigned long ulong_value; + long long long_long_value; unsigned long long ulong_long_value; long double long_double_value; const void *pointer_value; @@ -881,12 +890,18 @@ 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(long long value) + : type(LONG_LONG), long_long_value(value), formatter(0) {} + Arg(unsigned long long value) + : type(ULONG_LONG), 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) : type(LONG_DOUBLE), long_double_value(value), formatter(0) {} - Arg(Char value) : type(CHAR), int_value(value), formatter(0) {} + Arg(char value) : type(CHAR), int_value(value), formatter(0) {} + Arg(wchar_t value) + : type(CHAR), int_value(internal::CharTraits::ConvertWChar(value)), + formatter(0) {} Arg(const Char *value) : type(STRING), formatter(0) { string.value = value; @@ -908,6 +923,11 @@ class BasicFormatter { string.size = value.size(); } + Arg(StringRef value) : type(STRING), formatter(0) { + string.value = value.c_str(); + string.size = value.size(); + } + template Arg(const T &value) : type(CUSTOM), formatter(0) { custom.value = &value; @@ -938,16 +958,18 @@ class BasicFormatter { int num_open_braces_; int next_arg_index_; + typedef unsigned long long ULongLong; + friend class internal::FormatterProxy; - // Forbid copying other than from a temporary. Do not implement. - BasicFormatter(BasicFormatter &); + // Forbid copying from a temporary as in the following example: + // fmt::Formatter<> f = Format("test"); // not allowed + // This is done because BasicFormatter objects should normally exist + // only as temporaries returned by one of the formatting functions. + // Do not implement. + BasicFormatter(const BasicFormatter &); BasicFormatter& operator=(const BasicFormatter &); - void Add(const Arg &arg) { - args_.push_back(&arg); - } - void ReportError(const Char *s, StringRef message) const; unsigned ParseUInt(const Char *&s) const; @@ -957,6 +979,8 @@ class BasicFormatter { void CheckSign(const Char *&s, const Arg &arg); + // Parses the format string and performs the actual formatting, + // writing the output to writer_. void DoFormat(); struct Proxy { @@ -980,27 +1004,35 @@ class BasicFormatter { public: // Constructs a formatter with a writer to be used for output and a format - // format string. + // string. BasicFormatter(BasicWriter &w, const Char *format = 0) : writer_(&w), format_(format) {} +#if FMT_USE_INITIALIZER_LIST + // Constructs a formatter with formatting arguments. + BasicFormatter(BasicWriter &w, + const Char *format, std::initializer_list args) + : writer_(&w), format_(format) { + args_.reserve(args.size()); + for (const Arg &arg: args) + args_.push_back(&arg); + } +#endif + + // Performs formatting if the format string is non-null. The format string + // can be null if its ownership has been transferred to another formatter. ~BasicFormatter() { CompleteFormatting(); } - // Constructs a formatter from a proxy object. - BasicFormatter(const Proxy &p) : writer_(p.writer), format_(p.format) {} - - operator Proxy() { - const Char *format = format_; - format_ = 0; - return Proxy(writer_, format); + BasicFormatter(BasicFormatter &f) : writer_(f.writer_), format_(f.format_) { + f.format_ = 0; } // Feeds an argument to a formatter. BasicFormatter &operator<<(const Arg &arg) { arg.formatter = this; - Add(arg); + args_.push_back(&arg); return *this; } @@ -1089,7 +1121,8 @@ class NoAction { // Formats an error message and prints it to stdout. fmt::Formatter ReportError(const char *format) { - return fmt::Formatter(format); + fmt::Formatter f(format); + return f; } ReportError("File not found: {}") << path; @@ -1102,16 +1135,9 @@ class Formatter : private Action, public BasicFormatter { bool inactive_; // Forbid copying other than from a temporary. Do not implement. - Formatter(Formatter &); + Formatter(const Formatter &); Formatter& operator=(const Formatter &); - struct Proxy { - const Char *format; - Action action; - - Proxy(const Char *fmt, Action a) : format(fmt), action(a) {} - }; - public: /** \rst @@ -1127,10 +1153,10 @@ class Formatter : private Action, public BasicFormatter { inactive_(false) { } - // Constructs a formatter from a proxy object. - Formatter(const Proxy &p) - : Action(p.action), BasicFormatter(writer_, p.format), + Formatter(Formatter &f) + : Action(f), BasicFormatter(writer_, f.TakeFormatString()), inactive_(false) { + f.inactive_ = true; } /** @@ -1142,12 +1168,6 @@ class Formatter : private Action, public BasicFormatter { (*this)(writer_); } } - - // Converts the formatter into a proxy object. - operator Proxy() { - inactive_ = true; - return Proxy(this->TakeFormatString(), *this); - } }; /** @@ -1160,7 +1180,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; @@ -1196,10 +1216,9 @@ 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_; } + const char *c_str() const { return str_; } + std::string str() const { return str_; } }; /** @@ -1224,11 +1243,13 @@ class FormatInt { \endrst */ inline Formatter<> Format(StringRef format) { - return Formatter<>(format); + Formatter<> f(format); + return f; } inline Formatter Format(WStringRef format) { - return Formatter(format); + Formatter f(format); + return f; } /** A formatting action that writes formatted output to stdout. */ @@ -1244,14 +1265,17 @@ class Write { // Example: // Print("Elapsed time: {0:.2f} seconds") << 1.23; inline Formatter Print(StringRef format) { - return Formatter(format); + Formatter f(format); + return f; } } -#ifdef __GNUC__ -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#ifdef FMT_GCC_DIAGNOSTIC # pragma GCC diagnostic pop #endif + +#if _MSC_VER +# pragma warning(pop) #endif #endif // FORMAT_H_ From 0616edd08ea78f17670226385345795ab29a54c3 Mon Sep 17 00:00:00 2001 From: Gregory Czajkowski Date: Tue, 10 Dec 2013 17:45:42 -0800 Subject: [PATCH 5/5] add support for better error message back --- format.cc | 464 +++++++++++++++++++++++++++++------------------------- format.h | 23 ++- 2 files changed, 272 insertions(+), 215 deletions(-) diff --git a/format.cc b/format.cc index b273dbd2b6a0..8466358c35eb 100644 --- a/format.cc +++ b/format.cc @@ -411,246 +411,252 @@ 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; 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') { + // 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 '0' requires numeric argument"); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; + ReportError(s, "format specifier '#' requires numeric argument"); + spec.flags_ |= HASH_FLAG; + ++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 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); - ULongLong 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 LONG_LONG: - if (precision_arg.long_long_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.long_long_value; - break; - case ULONG_LONG: - 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); + ULongLong 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 LONG_LONG: + if (precision_arg.long_long_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.long_long_value; + break; + case ULONG_LONG: + 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"); } - 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++); } - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + start = 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 LONG_LONG: - writer.FormatInt(arg.long_long_value, spec); - break; - case ULONG_LONG: - 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); + // 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 LONG_LONG: + writer.FormatInt(arg.long_long_value, spec); + break; + case ULONG_LONG: + 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 { - std::fill_n(out + 1, spec.width_ - 1, fill); + out = writer.GrowBuffer(1); } - } else { - out = writer.GrowBuffer(1); + *out = arg.int_value; + 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); + 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; } - 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; - } + 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); } @@ -687,6 +693,18 @@ template void fmt::BasicFormatter::CheckSign( template void fmt::BasicFormatter::DoFormat(); +#ifdef FMT_FORMAT_STRING_IN_ERRORS +std::string fmt::FormatErrorMessage(const std::string &message, const char *format) { + fmt::Writer w; + w << "error: " << message << " while parsing format string " << format; + return w.str(); +} +#else +std::string fmt::FormatErrorMessage(const std::string &message, const char *format) { + return message; +} +#endif + // Explicit instantiations for wchar_t. template void fmt::BasicWriter::FormatDouble( @@ -719,3 +737,21 @@ template void fmt::BasicFormatter::CheckSign( const wchar_t *&s, const Arg &arg); template void fmt::BasicFormatter::DoFormat(); + +#ifdef FMT_FORMAT_STRING_IN_ERRORS +std::string fmt::FormatErrorMessage(const std::string &message, const wchar_t *format) { + + int size = FMT_SNPRINTF(NULL, 0, "%ls", format); + char* buf = (char*)malloc(size+1); + FMT_SNPRINTF(buf, size+1, "%ls", format); + + fmt::Writer w; + w << "error: " << message << " while parsing format string " << buf; + free(buf); + return w.str(); +} +#else +std::string fmt::FormatErrorMessage(const std::string &message, const wchar_t *format) { + return message; +} +#endif diff --git a/format.h b/format.h index 2ac0bdf258b0..786049f40487 100644 --- a/format.h +++ b/format.h @@ -315,6 +315,20 @@ class FormatError : public std::runtime_error { : std::runtime_error(message) {} }; +std::string FormatErrorMessage(const std::string &message, const char *format); +std::string FormatErrorMessage(const std::string &message, const wchar_t *format); + +template +class BasicFormatError : public std::runtime_error { +private: + std::basic_string format_; +public: + explicit BasicFormatError(const std::string &message, const Char *format) + : std::runtime_error(fmt::FormatErrorMessage(message, format)), format_(format){} + virtual ~BasicFormatError() throw() {} + const Char *format() const { return format_.c_str(); } +}; + enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC }; @@ -402,6 +416,11 @@ class IntFormatter : public SpecT { */ IntFormatter > bin(int value); +/** + Returns an integer formatter that formats the value in base 2. + */ +IntFormatter > binu(int value); + /** Returns an integer formatter that formats the value in base 8. */ @@ -439,7 +458,9 @@ IntFormatter > pad( inline IntFormatter > bin(TYPE value) { \ return IntFormatter >(value, TypeSpec<'b'>()); \ } \ - \ +inline IntFormatter > binu(TYPE value) { \ + return IntFormatter >(value, TypeSpec<'B'>()); \ +} \ inline IntFormatter > oct(TYPE value) { \ return IntFormatter >(value, TypeSpec<'o'>()); \ } \