diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index e662d0e3..e0dd0537 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -51,12 +51,8 @@ // Dummy implementations of strerror_r and strerror_s called if corresponding // system functions are not available. -inline fmt::internal::null<> strerror_r(int, char*, ...) { - return {}; -} -inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { - return {}; -} +inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; } +inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; } FMT_BEGIN_NAMESPACE namespace internal { @@ -250,20 +246,6 @@ template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) { return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; } -template -int format_float(char* buf, std::size_t size, const char* format, int precision, - T value) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); -#endif - // Suppress the warning about nonliteral format string. - auto snprintf_ptr = FMT_SNPRINTF; - return precision < 0 ? snprintf_ptr(buf, size, format, value) - : snprintf_ptr(buf, size, format, precision, value); -} - template const char basic_data::digits[] = "0001020304050607080910111213141516171819" @@ -276,9 +258,9 @@ template const char basic_data::hex_digits[] = "0123456789abcdef"; #define FMT_POWERS_OF_10(factor) \ - factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, (factor) * 100000, \ - (factor) * 1000000, (factor) * 10000000, (factor) * 100000000, \ - (factor) * 1000000000 + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 template const uint64_t basic_data::powers_of_10_64[] = { @@ -1121,78 +1103,99 @@ bool grisu_format(Double value, buffer& buf, int precision, return true; } -template -char* sprintf_format(Double value, internal::buffer& buf, - sprintf_specs specs) { +template +int sprintf_format(Float value, int precision, float_spec spec, + buffer& buf) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. - FMT_ASSERT(buf.capacity() != 0, "empty buffer"); + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - // Build format string. - enum { max_format_size = 10 }; // longest format: %#-*.*Lg + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (spec.format == float_format::general) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // Ths longest format is "%#.*Le". char format[max_format_size]; char* format_ptr = format; *format_ptr++ = '%'; - if (specs.alt || !specs.type) *format_ptr++ = '#'; - if (specs.precision >= 0) { + if (spec.alt) *format_ptr++ = '#'; + if (precision > 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; } - if (std::is_same::value) *format_ptr++ = 'L'; - - char type = specs.type; - - if (type == '%') - type = 'f'; - else if (type == 0 || type == 'n') - type = 'g'; - if (FMT_MSC_VER && type == 'F') - type = 'f'; // // MSVC's printf doesn't support 'F'. - *format_ptr++ = type; + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = spec.format != float_format::hex + ? (spec.format == float_format::fixed ? 'f' : 'e') + : (spec.upper ? 'A' : 'a'); *format_ptr = '\0'; // Format using snprintf. - char* start = nullptr; - char* decimal_point_pos = nullptr; + auto offset = buf.size(); for (;;) { - std::size_t buffer_size = buf.capacity(); - start = &buf[0]; - int result = - format_float(start, buffer_size, format, specs.precision, value); - if (result >= 0) { - unsigned n = internal::to_unsigned(result); - if (n < buf.capacity()) { - // Find the decimal point. - auto p = buf.data(), end = p + n; - if (*p == '+' || *p == '-') ++p; - if (specs.type != 'a' && specs.type != 'A') { - while (p < end && *p >= '0' && *p <= '9') ++p; - if (p < end && *p != 'e' && *p != 'E') { - decimal_point_pos = p; - if (!specs.type) { - // Keep only one trailing zero after the decimal point. - ++p; - if (*p == '0') ++p; - while (p != end && *p >= '1' && *p <= '9') ++p; - char* where = p; - while (p != end && *p == '0') ++p; - if (p == end || *p < '0' || *p > '9') { - if (p != end) std::memmove(where, p, to_unsigned(end - p)); - n -= static_cast(p - where); - } - } - } - } - buf.resize(n); - break; // The buffer is large enough - continue with formatting. - } - buf.reserve(n + 1); - } else { - // If result is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buf.reserve(buf.capacity() + 1); + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + auto snprintf_ptr = FMT_SNPRINTF; + int result = precision > 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + buf.reserve(capacity + 1); // The buffer will grow exponentially. + continue; } + unsigned size = to_unsigned(result); + if (size > capacity) { + buf.reserve(size + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (spec.format == float_format::fixed) { + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, fraction_size); + buf.resize(size - 1); + return -fraction_size; + } + if (spec.format == float_format::hex) { + buf.resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + assert(sign == '+' || sign == '-'); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + assert(is_digit(*p)); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + int fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, fraction_size); + buf.resize(fraction_size + offset + 1); + exp -= fraction_size; + } + return exp; } - return decimal_point_pos; } } // namespace internal diff --git a/include/fmt/format.h b/include/fmt/format.h index 3d90387e..c7f8eb74 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1063,10 +1063,9 @@ namespace internal { // A floating-point presentation format. enum class float_format { - shortest, // Shortest round-trip format. - general, // General: exponent notation or fixed point based on magnitude. - exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. - fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. hex }; @@ -1075,6 +1074,7 @@ struct float_spec { bool upper; bool locale; bool percent; + bool alt; }; struct gen_digits_params { @@ -1087,7 +1087,7 @@ struct gen_digits_params { // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template It write_exponent(int exp, It it) { - FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range"); + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *it++ = static_cast('-'); exp = -exp; @@ -1095,7 +1095,9 @@ template It write_exponent(int exp, It it) { *it++ = static_cast('+'); } if (exp >= 100) { - *it++ = static_cast(static_cast('0' + exp / 100)); + const char* top = data::digits + (exp / 100) * 2; + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); exp %= 100; } const char* d = data::digits + exp * 2; @@ -1104,7 +1106,7 @@ template It write_exponent(int exp, It it) { return it; } -template class grisu_writer { +template class float_writer { private: // The number is given as v = digits_ * pow(10, exp_). const char* digits_; @@ -1178,7 +1180,7 @@ template class grisu_writer { } public: - grisu_writer(const char* digits, int num_digits, int exp, + float_writer(const char* digits, int num_digits, int exp, gen_digits_params params, Char decimal_point) : digits_(digits), num_digits_(num_digits), @@ -1218,26 +1220,15 @@ inline bool grisu_format(Double, buffer&, int, unsigned, int&) { return false; } -struct sprintf_specs { - int precision; - char type; - bool alt : 1; - - template - constexpr sprintf_specs(basic_format_specs specs) - : precision(specs.precision), type(specs.type), alt(specs.alt) {} - - constexpr bool has_precision() const { return precision >= 0; } -}; - -template -char* sprintf_format(Double, internal::buffer&, sprintf_specs); +template +int sprintf_format(Float value, int precision, float_spec spec, + buffer& buf); template <> -inline char* sprintf_format(float value, internal::buffer& buf, - sprintf_specs specs) { - // printf does not have a float format specifier, it only supports double. - return sprintf_format(value, buf, specs); +inline int sprintf_format(float value, int precision, float_spec spec, + buffer& buf) { + // sprintf does not support float so convert to double. + return sprintf_format(value, precision, spec, buf); } template @@ -1399,6 +1390,20 @@ void arg_map::init(const basic_format_args& args) { } } +template struct inf_or_nan_writer { + sign_t sign; + const char* str; + static constexpr size_t str_size = 3; + + size_t size() const { return str_size + (sign ? 1 : 0); } + size_t width() const { return size(); } + + template void operator()(It&& it) const { + if (sign) *it++ = static_cast(data::signs[sign]); + it = copy_str(str, str + str_size, it); + } +}; + // This template provides operations for formatting and writing data into a // character range. template class basic_writer { @@ -1409,7 +1414,7 @@ template class basic_writer { private: iterator out_; // Output iterator. - internal::locale_ref locale_; + locale_ref locale_; // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to out_. @@ -1429,7 +1434,7 @@ template class basic_writer { template void operator()(It&& it) const { if (prefix.size() != 0) - it = internal::copy_str(prefix.begin(), prefix.end(), it); + it = copy_str(prefix.begin(), prefix.end(), it); it = std::fill_n(it, padding, fill); f(it); } @@ -1440,18 +1445,18 @@ template class basic_writer { // where are written by f(it). template void write_int(int num_digits, string_view prefix, format_specs specs, F f) { - std::size_t size = prefix.size() + internal::to_unsigned(num_digits); + std::size_t size = prefix.size() + to_unsigned(num_digits); char_type fill = specs.fill[0]; std::size_t padding = 0; if (specs.align == align::numeric) { - auto unsiged_width = internal::to_unsigned(specs.width); + auto unsiged_width = to_unsigned(specs.width); if (unsiged_width > size) { padding = unsiged_width - size; size = unsiged_width; } } else if (specs.precision > num_digits) { - size = prefix.size() + internal::to_unsigned(specs.precision); - padding = internal::to_unsigned(specs.precision - num_digits); + size = prefix.size() + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); fill = static_cast('0'); } if (specs.align == align::none) specs.align = align::right; @@ -1461,14 +1466,13 @@ template class basic_writer { // Writes a decimal integer. template void write_decimal(Int value) { auto abs_value = static_cast>(value); - bool is_negative = internal::is_negative(value); + bool negative = is_negative(value); // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (is_negative) abs_value = ~abs_value + 1; - int num_digits = internal::count_digits(abs_value); - auto&& it = - reserve((is_negative ? 1 : 0) + static_cast(num_digits)); - if (is_negative) *it++ = static_cast('-'); - it = internal::format_decimal(it, abs_value, num_digits); + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto&& it = reserve((negative ? 1 : 0) + static_cast(num_digits)); + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits); } // The handle_int_type_spec handler that writes an integer. @@ -1488,7 +1492,7 @@ template class basic_writer { specs(s), abs_value(static_cast(value)), prefix_size(0) { - if (internal::is_negative(value)) { + if (is_negative(value)) { prefix[0] = '-'; ++prefix_size; abs_value = 0 - abs_value; @@ -1508,7 +1512,7 @@ template class basic_writer { }; void on_dec() { - int num_digits = internal::count_digits(abs_value); + int num_digits = count_digits(abs_value); writer.write_int(num_digits, get_prefix(), specs, dec_writer{abs_value, num_digits}); } @@ -1518,8 +1522,8 @@ template class basic_writer { int num_digits; template void operator()(It&& it) const { - it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits, - self.specs.type != 'x'); + it = format_uint<4, char_type>(it, self.abs_value, num_digits, + self.specs.type != 'x'); } }; @@ -1528,7 +1532,7 @@ template class basic_writer { prefix[prefix_size++] = '0'; prefix[prefix_size++] = specs.type; } - int num_digits = internal::count_digits<4>(abs_value); + int num_digits = count_digits<4>(abs_value); writer.write_int(num_digits, get_prefix(), specs, hex_writer{*this, num_digits}); } @@ -1538,7 +1542,7 @@ template class basic_writer { int num_digits; template void operator()(It&& it) const { - it = internal::format_uint(it, abs_value, num_digits); + it = format_uint(it, abs_value, num_digits); } }; @@ -1547,13 +1551,13 @@ template class basic_writer { prefix[prefix_size++] = '0'; prefix[prefix_size++] = static_cast(specs.type); } - int num_digits = internal::count_digits<1>(abs_value); + int num_digits = count_digits<1>(abs_value); writer.write_int(num_digits, get_prefix(), specs, bin_writer<1>{abs_value, num_digits}); } void on_oct() { - int num_digits = internal::count_digits<3>(abs_value); + int num_digits = count_digits<3>(abs_value); if (specs.alt && specs.precision <= num_digits && abs_value != 0) { // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. @@ -1577,7 +1581,7 @@ template class basic_writer { // index 0. unsigned digit_index = 0; std::string::const_iterator group = groups.cbegin(); - it = internal::format_decimal( + it = format_decimal( it, abs_value, size, [this, s, &group, &digit_index](char_type*& buffer) { if (*group <= 0 || ++digit_index % *group != 0 || @@ -1589,17 +1593,17 @@ template class basic_writer { } buffer -= s.size(); std::uninitialized_copy(s.data(), s.data() + s.size(), - internal::make_checked(buffer, s.size())); + make_checked(buffer, s.size())); }); } }; void on_num() { - std::string groups = internal::grouping(writer.locale_); + std::string groups = grouping(writer.locale_); if (groups.empty()) return on_dec(); - auto sep = internal::thousands_sep(writer.locale_); + auto sep = thousands_sep(writer.locale_); if (!sep) return on_dec(); - int num_digits = internal::count_digits(abs_value); + int num_digits = count_digits(abs_value); int size = num_digits; std::string::const_iterator group = groups.cbegin(); while (group != groups.cend() && num_digits > *group && *group > 0 && @@ -1619,59 +1623,17 @@ template class basic_writer { } }; - enum { inf_size = 3 }; // This is an enum to workaround a bug in MSVC. - - struct inf_or_nan_writer { - sign_t sign; - bool as_percentage; - const char* str; - - size_t size() const { - return static_cast(inf_size + (sign ? 1 : 0) + - (as_percentage ? 1 : 0)); - } - size_t width() const { return size(); } - - template void operator()(It&& it) const { - if (sign) *it++ = static_cast(data::signs[sign]); - it = internal::copy_str( - str, str + static_cast(inf_size), it); - if (as_percentage) *it++ = static_cast('%'); - } - }; - - struct double_writer { - sign_t sign; - internal::buffer& buffer; - char* decimal_point_pos; - char_type decimal_point; - - size_t size() const { return buffer.size() + (sign ? 1 : 0); } - size_t width() const { return size(); } - - template void operator()(It&& it) { - if (sign) *it++ = static_cast(data::signs[sign]); - auto begin = buffer.begin(); - if (decimal_point_pos) { - it = internal::copy_str(begin, decimal_point_pos, it); - *it++ = decimal_point; - begin = decimal_point_pos + 1; - } - it = internal::copy_str(begin, buffer.end(), it); - } - }; - template struct str_writer { const Char* s; size_t size_; size_t size() const { return size_; } size_t width() const { - return internal::count_code_points(basic_string_view(s, size_)); + return count_code_points(basic_string_view(s, size_)); } template void operator()(It&& it) const { - it = internal::copy_str(s, s + size_, it); + it = copy_str(s, s + size_, it); } }; @@ -1685,13 +1647,12 @@ template class basic_writer { template void operator()(It&& it) const { *it++ = static_cast('0'); *it++ = static_cast('x'); - it = internal::format_uint<4, char_type>(it, value, num_digits); + it = format_uint<4, char_type>(it, value, num_digits); } }; public: - explicit basic_writer(Range out, - internal::locale_ref loc = internal::locale_ref()) + explicit basic_writer(Range out, locale_ref loc = locale_ref()) : out_(out.begin()), locale_(loc) {} iterator out() const { return out_; } @@ -1735,15 +1696,13 @@ template class basic_writer { void write(uint128_t value) { write_decimal(value); } #endif - // Writes a formatted integer. template void write_int(T value, const Spec& spec) { - internal::handle_int_type_spec(spec.type, - int_writer(*this, value, spec)); + handle_int_type_spec(spec.type, int_writer(*this, value, spec)); } template ::value)> - void write(T value, const format_specs& specs = {}) { + void write(T value, format_specs specs = {}) { auto sign = specs.sign; if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. sign = sign::minus; @@ -1754,67 +1713,66 @@ template class basic_writer { float_spec fspec = parse_float_type_spec(specs.type); if (!std::isfinite(value)) { - const char* str = std::isinf(value) ? (fspec.upper ? "INF" : "inf") - : (fspec.upper ? "NAN" : "nan"); - return write_padded(specs, inf_or_nan_writer{sign, fspec.percent, str}); + auto str = std::isinf(value) ? (fspec.upper ? "INF" : "inf") + : (fspec.upper ? "NAN" : "nan"); + return write_padded(specs, inf_or_nan_writer{sign, str}); } - if (fspec.percent) value *= 100; - - memory_buffer buffer; - int exp = 0; - int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; - int num_digits = - fspec.format == float_format::exp ? precision + 1 : precision; - unsigned options = 0; - if (fspec.format == float_format::fixed) options |= grisu_options::fixed; - if (const_check(sizeof(value) == sizeof(float))) - options |= grisu_options::binary32; - bool use_grisu = internal::use_grisu() && - (specs.type != 'a' && specs.type != 'A') && - grisu_format(static_cast(value), buffer, - num_digits, options, exp); - char* decimal_point_pos = nullptr; - if (!use_grisu) decimal_point_pos = sprintf_format(value, buffer, specs); - - if (fspec.percent) { - buffer.push_back('%'); - --exp; // Adjust decimal place position. - } - format_specs as = specs; if (specs.align == align::numeric) { if (sign) { auto&& it = reserve(1); *it++ = static_cast(data::signs[sign]); sign = sign::none; - if (as.width) --as.width; + if (specs.width != 0) --specs.width; } - as.align = align::right; + specs.align = align::right; } else if (specs.align == align::none) { - as.align = align::right; + specs.align = align::right; } + + memory_buffer buffer; + if (fspec.format == float_format::hex) { + if (sign) buffer.push_back(data::signs[sign]); + fspec.alt = specs.alt; + sprintf_format(value, specs.precision, fspec, buffer); + write_padded(specs, str_writer{buffer.data(), buffer.size()}); + return; + } + int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + int num_digits = + fspec.format == float_format::exp ? precision + 1 : precision; + unsigned options = 0; + if (fspec.format == float_format::fixed) options |= grisu_options::fixed; + if (const_check(std::is_same())) + options |= grisu_options::binary32; + if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) value *= 100; + int exp = 0; + bool use_grisu = internal::use_grisu() && + grisu_format(static_cast(value), buffer, + num_digits, options, exp); + if (!use_grisu) exp = sprintf_format(value, precision, fspec, buffer); + + if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) { + buffer.push_back('%'); + --exp; // Adjust decimal place position. + } + auto params = gen_digits_params(); + params.sign = sign; + params.format = fspec.format; + params.num_digits = num_digits; + params.trailing_zeros = + (precision != 0 && + (!specs.type || fspec.format == float_format::fixed || + fspec.format == float_format::exp)) || + specs.alt; + params.upper = fspec.upper; + num_digits = static_cast(buffer.size()); char_type point = fspec.locale ? decimal_point(locale_) : static_cast('.'); - if (use_grisu) { - auto params = gen_digits_params(); - params.sign = sign; - params.format = fspec.format; - params.num_digits = num_digits; - params.trailing_zeros = - (precision != 0 && - (!specs.type || fspec.format == float_format::fixed || - fspec.format == float_format::exp)) || - specs.alt; - params.upper = fspec.upper; - num_digits = static_cast(buffer.size()); - write_padded(as, grisu_writer(buffer.data(), num_digits, exp, - params, point)); - } else { - write_padded(as, double_writer{sign, buffer, decimal_point_pos, point}); - } + write_padded(specs, float_writer(buffer.data(), num_digits, exp, + params, point)); } - // Writes a character to the buffer. void write(char value) { auto&& it = reserve(1); *it++ = value; @@ -1826,10 +1784,9 @@ template class basic_writer { *it++ = value; } - // Writes value to the buffer. void write(string_view value) { auto&& it = reserve(value.size()); - it = internal::copy_str(value.begin(), value.end(), it); + it = copy_str(value.begin(), value.end(), it); } void write(wstring_view value) { static_assert(std::is_same::value, ""); @@ -1837,26 +1794,23 @@ template class basic_writer { it = std::copy(value.begin(), value.end(), it); } - // Writes a formatted string. template void write(const Char* s, std::size_t size, const format_specs& specs) { write_padded(specs, str_writer{s, size}); } template - void write(basic_string_view s, - const format_specs& specs = format_specs()) { + void write(basic_string_view s, const format_specs& specs = {}) { const Char* data = s.data(); std::size_t size = s.size(); - if (specs.precision >= 0 && internal::to_unsigned(specs.precision) < size) - size = - internal::code_point_index(s, internal::to_unsigned(specs.precision)); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); write(data, size, specs); } template void write_pointer(UIntPtr value, const format_specs* specs) { - int num_digits = internal::count_digits<4>(value); + int num_digits = count_digits<4>(value); auto pw = pointer_writer{value, num_digits}; if (!specs) return pw(reserve(to_unsigned(num_digits) + 2)); format_specs specs_copy = *specs; @@ -3486,8 +3440,7 @@ inline std::basic_string internal::vformat( */ template inline std::size_t formatted_size(string_view format_str, const Args&... args) { - auto it = format_to(internal::counting_iterator(), format_str, args...); - return it.count(); + return format_to(internal::counting_iterator(), format_str, args...).count(); } #if FMT_USE_USER_DEFINED_LITERALS diff --git a/src/format.cc b/src/format.cc index 053b1349..22238257 100644 --- a/src/format.cc +++ b/src/format.cc @@ -36,11 +36,11 @@ template FMT_API std::string internal::vformat( template FMT_API format_context::iterator internal::vformat_to( internal::buffer&, string_view, basic_format_args); -template FMT_API char* internal::sprintf_format(double, internal::buffer&, - sprintf_specs); -template FMT_API char* internal::sprintf_format(long double, - internal::buffer&, - sprintf_specs); +template FMT_API int internal::sprintf_format(double, int, internal::float_spec, + internal::buffer&); +template FMT_API int internal::sprintf_format(long double, int, + internal::float_spec, + internal::buffer&); // Explicit instantiations for wchar_t. diff --git a/support/Vagrantfile b/support/Vagrantfile index de0fcb9e..24f166a1 100644 --- a/support/Vagrantfile +++ b/support/Vagrantfile @@ -3,7 +3,7 @@ # A vagrant config for testing against gcc-4.8. Vagrant.configure("2") do |config| - config.vm.box = "ubuntu/trusty64" + config.vm.box = "ubuntu/xenial64" config.vm.provider "virtualbox" do |vb| vb.memory = "4096"