Merge pull request #1 from fmtlib/master

Rebase master.
This commit is contained in:
Attila M. Szilagyi 2020-01-24 18:37:24 -08:00 committed by GitHub
commit fcd72eb81b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 353 additions and 172 deletions

View File

@ -85,6 +85,19 @@ Compatibility
.. doxygentypedef:: fmt::string_view .. doxygentypedef:: fmt::string_view
.. doxygentypedef:: fmt::wstring_view .. doxygentypedef:: fmt::wstring_view
Locale
------
All formatting is locale-independent by default. Use the ``'n'`` format
specifier to insert the appropriate number separator characters from the
locale::
#include <fmt/core.h>
#include <locale>
std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:n}", 1000000); // s == "1,000,000"
.. _format-api: .. _format-api:
Format API Format API

View File

@ -76,7 +76,7 @@ The general form of a *standard format specifier* is:
.. productionlist:: sf .. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`] format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`]
fill: <a character other than '{', '}' or '\0'> fill: <a character other than '{' or '}'>
align: "<" | ">" | "^" align: "<" | ">" | "^"
sign: "+" | "-" | " " sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}" width: `integer` | "{" `arg_id` "}"
@ -84,11 +84,11 @@ The general form of a *standard format specifier* is:
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s" type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X" int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
The *fill* character can be any character other than '{', '}' or '\\0'. The The *fill* character can be any Unicode code point other than ``'{'`` or
presence of a fill character is signaled by the character following it, which ``'}'``. The presence of a fill character is signaled by the character following
must be one of the alignment options. If the second character of *format_spec* it, which must be one of the alignment options. If the second character of
is not a valid alignment option, then it is assumed that both the fill character *format_spec* is not a valid alignment option, then it is assumed that both the
and the alignment option are absent. fill character and the alignment option are absent.
The meaning of the various alignment options is as follows: The meaning of the various alignment options is as follows:
@ -320,71 +320,93 @@ following examples.
Accessing arguments by position:: Accessing arguments by position::
format("{0}, {1}, {2}", 'a', 'b', 'c'); fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c" // Result: "a, b, c"
format("{}, {}, {}", 'a', 'b', 'c'); fmt::format("{}, {}, {}", 'a', 'b', 'c');
// Result: "a, b, c" // Result: "a, b, c"
format("{2}, {1}, {0}", 'a', 'b', 'c'); fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
// Result: "c, b, a" // Result: "c, b, a"
format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
// Result: "abracadabra" // Result: "abracadabra"
Aligning the text and specifying a width:: Aligning the text and specifying a width::
format("{:<30}", "left aligned"); fmt::format("{:<30}", "left aligned");
// Result: "left aligned " // Result: "left aligned "
format("{:>30}", "right aligned"); fmt::format("{:>30}", "right aligned");
// Result: " right aligned" // Result: " right aligned"
format("{:^30}", "centered"); fmt::format("{:^30}", "centered");
// Result: " centered " // Result: " centered "
format("{:*^30}", "centered"); // use '*' as a fill char fmt::format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********" // Result: "***********centered***********"
Dynamic width:: Dynamic width::
format("{:<{}}", "left aligned", 30); fmt::format("{:<{}}", "left aligned", 30);
// Result: "left aligned " // Result: "left aligned "
Dynamic precision:: Dynamic precision::
format("{:.{}f}", 3.14, 1); fmt::format("{:.{}f}", 3.14, 1);
// Result: "3.1" // Result: "3.1"
Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:: Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::
format("{:+f}; {:+f}", 3.14, -3.14); // show it always fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
// Result: "+3.140000; -3.140000" // Result: "+3.140000; -3.140000"
format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
// Result: " 3.140000; -3.140000" // Result: " 3.140000; -3.140000"
format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}' fmt::format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
// Result: "3.140000; -3.140000" // Result: "3.140000; -3.140000"
Replacing ``%x`` and ``%o`` and converting the value to different bases:: Replacing ``%x`` and ``%o`` and converting the value to different bases::
format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010" // Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
// with 0x or 0 or 0b as prefix: // with 0x or 0 or 0b as prefix:
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42); fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010" // Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
Padded hex byte with prefix and always prints both hex characters:: Padded hex byte with prefix and always prints both hex characters::
format("{:#04x}", 0); fmt::format("{:#04x}", 0);
// Result: "0x00" // Result: "0x00"
.. ifconfig:: False Box drawing using Unicode fill::
Using the comma as a thousands separator:: fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
format("{:,}", 1234567890); prints::
'1,234,567,890'
┌────────────────────┐
│ Hello, world! │
└────────────────────┘
Using type-specific formatting:: Using type-specific formatting::
>>> import datetime #include <fmt/chrono.h>
>>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)
Format("{:%Y-%m-%d %H:%M:%S}") << d) auto t = tm();
'2010-07-04 12:15:58' t.tm_year = 2010 - 1900;
t.tm_mon = 6;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58
Using the comma as a thousands separator::
#include <fmt/locale.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:n}", 1234567890);
// s == "1,234,567,890"
.. ifconfig:: False
Nesting arguments and more complex examples:: Nesting arguments and more complex examples::

View File

@ -114,6 +114,22 @@ fmt can be installed on Linux, macOS and Windows with
conda install -c conda-forge fmt conda install -c conda-forge fmt
Vcpkg
=====
You can download and install fmt using the `vcpkg
<https://github.com/Microsoft/vcpkg>`__ dependency manager::
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install fmt
The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please `create an issue or pull
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
Android NDK Android NDK
=========== ===========

View File

@ -8,14 +8,14 @@
#ifndef FMT_CHRONO_H_ #ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_ #define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <locale> #include <locale>
#include <sstream> #include <sstream>
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled. // Enable safe chrono durations, unless explicitly disabled.
@ -495,12 +495,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_text(ptr - 1, ptr); handler.on_text(ptr - 1, ptr);
break; break;
case 'n': { case 'n': {
const char newline[] = "\n"; const Char newline[]{'\n', 0};
handler.on_text(newline, newline + 1); handler.on_text(newline, newline + 1);
break; break;
} }
case 't': { case 't': {
const char tab[] = "\t"; const Char tab[]{'\t', 0};
handler.on_text(tab, tab + 1); handler.on_text(tab, tab + 1);
break; break;
} }
@ -759,18 +759,30 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms)); return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
} }
template <typename Rep, typename OutputIt> template <typename Char, typename Rep, typename OutputIt>
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) { OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision); const Char pr_f[]{'{', ':', '.', '{', '}', 'f', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}", if (precision >= 0) return format_to(out, pr_f, val, precision);
const Char fp_f[]{'{', ':', 'g', '}', 0};
const Char format[]{'{', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val); val);
} }
template <typename Period, typename OutputIt> template <typename Char, typename Period, typename OutputIt>
static OutputIt format_chrono_duration_unit(OutputIt out) { OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit); if (const char* unit = get_units<Period>()) {
if (Period::den == 1) return format_to(out, "[{}]s", Period::num); string_view s(unit);
return format_to(out, "[{}/{}]s", Period::num, Period::den); if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
const Char num_f[]{'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num);
const Char num_def_f[]{'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den);
} }
template <typename FormatContext, typename OutputIt, typename Rep, template <typename FormatContext, typename OutputIt, typename Rep,
@ -871,13 +883,13 @@ struct chrono_formatter {
void write_pinf() { std::copy_n("inf", 3, out); } void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); } void write_ninf() { std::copy_n("-inf", 4, out); }
void format_localized(const tm& time, const char* format) { void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan(); if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>(); auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale); auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os; std::basic_ostringstream<char_type> os;
os.imbue(locale); os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format)); facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str(); auto str = os.str();
std::copy(str.begin(), str.end(), out); std::copy(str.begin(), str.end(), out);
} }
@ -907,7 +919,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour(), 2); if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm(); auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24); time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH"); format_localized(time, 'H', 'O');
} }
void on_12_hour(numeric_system ns) { void on_12_hour(numeric_system ns) {
@ -916,7 +928,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour12(), 2); if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm(); auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12); time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI"); format_localized(time, 'I', 'O');
} }
void on_minute(numeric_system ns) { void on_minute(numeric_system ns) {
@ -925,7 +937,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(minute(), 2); if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm(); auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60); time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM"); format_localized(time, 'M', 'O');
} }
void on_second(numeric_system ns) { void on_second(numeric_system ns) {
@ -950,13 +962,12 @@ struct chrono_formatter {
} }
auto time = tm(); auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60); time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS"); format_localized(time, 'S', 'O');
} }
void on_12_hour_time() { void on_12_hour_time() {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
format_localized(time(), 'r');
format_localized(time(), "%r");
} }
void on_24_hour_time() { void on_24_hour_time() {
@ -980,16 +991,18 @@ struct chrono_formatter {
void on_am_pm() { void on_am_pm() {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
format_localized(time(), "%p"); format_localized(time(), 'p');
} }
void on_duration_value() { void on_duration_value() {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
write_sign(); write_sign();
out = format_chrono_duration_value(out, val, precision); out = format_duration_value<char_type>(out, val, precision);
} }
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); } void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
}; };
} // namespace internal } // namespace internal
@ -1024,7 +1037,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
} }
void on_error(const char* msg) { FMT_THROW(format_error(msg)); } void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; } void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
void on_align(align_t align) { f.specs.align = align; } void on_align(align_t align) { f.specs.align = align; }
void on_width(int width) { f.specs.width = width; } void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; } void on_precision(int _precision) { f.precision = _precision; }
@ -1088,8 +1101,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx); precision, precision_ref, ctx);
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision); out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out); internal::format_duration_unit<Char, Period>(out);
} else { } else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f( internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d); ctx, out, d);

View File

@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
internal::make_background_color<Char>(ts.get_background()); internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
vformat_to(buf, format_str, args); internal::vformat_to(buf, format_str, args);
if (has_style) { if (has_style) internal::reset_color<Char>(buf);
internal::reset_color<Char>(buf);
}
} }
} // namespace internal } // namespace internal
@ -540,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str, const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args); internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
@ -562,7 +560,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return vformat(ts, to_string_view(format_str), return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)}); internal::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -9,6 +9,7 @@
#define FMT_COMPILE_H_ #define FMT_COMPILE_H_
#include <vector> #include <vector>
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -549,7 +550,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
using range = buffer_range<Char>; using range = buffer_range<Char>;
using context = buffer_context<Char>; using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf, internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)}); make_format_args<context>(args...));
return to_string(buffer); return to_string(buffer);
} }
@ -561,8 +562,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf,
using char_type = typename CompiledFormat::char_type; using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>; using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>; using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>( return internal::cf::vformat_to<context>(range(out), cf,
range(out), cf, {make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename CompiledFormat, typename... Args,

View File

@ -15,7 +15,7 @@
#include <type_traits> #include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch. // The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60102 #define FMT_VERSION 60103
#ifdef __has_feature #ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x) # define FMT_HAS_FEATURE(x) __has_feature(x)
@ -217,7 +217,7 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other metafunctions for pre-C++14 systems. // Implementations of enable_if_t and other metafunctions for older systems.
template <bool B, class T = void> template <bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type; using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, class T, class F> template <bool B, class T, class F>
@ -229,6 +229,8 @@ template <typename T>
using remove_const_t = typename std::remove_const<T>::type; using remove_const_t = typename std::remove_const<T>::type;
template <typename T> template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type; using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; };
template <typename T> using type_identity_t = typename type_identity<T>::type;
struct monostate {}; struct monostate {};
@ -251,7 +253,7 @@ FMT_API void assert_fail(const char* file, int line, const char* message);
# define FMT_ASSERT(condition, message) \ # define FMT_ASSERT(condition, message) \
((condition) \ ((condition) \
? void() \ ? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message))) : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif # endif
#endif #endif
@ -266,7 +268,7 @@ template <typename T> struct std_string_view {};
#ifdef FMT_USE_INT128 #ifdef FMT_USE_INT128
// Do nothing. // Do nothing.
#elif defined(__SIZEOF_INT128__) #elif defined(__SIZEOF_INT128__) && !FMT_NVCC
# define FMT_USE_INT128 1 # define FMT_USE_INT128 1
using int128_t = __int128_t; using int128_t = __int128_t;
using uint128_t = __uint128_t; using uint128_t = __uint128_t;
@ -1220,7 +1222,13 @@ using wformat_context = buffer_context<wchar_t>;
such as `~fmt::vformat`. such as `~fmt::vformat`.
\endrst \endrst
*/ */
template <typename Context, typename... Args> class format_arg_store { template <typename Context, typename... Args>
class format_arg_store
#if FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private: private:
static const size_t num_args = sizeof...(Args); static const size_t num_args = sizeof...(Args);
static const bool is_packed = num_args < internal::max_packed_args; static const bool is_packed = num_args < internal::max_packed_args;
@ -1239,7 +1247,12 @@ template <typename Context, typename... Args> class format_arg_store {
: internal::is_unpacked_bit | num_args; : internal::is_unpacked_bit | num_args;
format_arg_store(const Args&... args) format_arg_store(const Args&... args)
: data_{internal::make_arg<is_packed, Context>(args)...} {} :
#if FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{internal::make_arg<is_packed, Context>(args)...} {
}
}; };
/** /**
@ -1426,21 +1439,23 @@ template <typename... Args, typename S, typename Char = char_t<S>>
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
make_args_checked(const S& format_str, make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args) { const remove_reference_t<Args>&... args) {
static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() || static_assert(
!std::is_reference<Args>())...>::value, all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
!std::is_reference<Args>::value)...>::value,
"passing views as lvalues is disallowed"); "passing views as lvalues is disallowed");
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str); check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
return {args...}; return {args...};
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat(basic_string_view<Char> format_str, std::basic_string<Char> vformat(
basic_format_args<buffer_context<Char>> args); basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args);
template <typename Char> template <typename Char>
typename buffer_context<Char>::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str, buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args); basic_format_args<buffer_context<type_identity_t<Char>>> args);
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
} // namespace internal } // namespace internal
@ -1471,8 +1486,9 @@ void arg(S, internal::named_arg<T, Char>) = delete;
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF( FMT_ENABLE_IF(
internal::is_contiguous_back_insert_iterator<OutputIt>::value)> internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
OutputIt vformat_to(OutputIt out, const S& format_str, OutputIt vformat_to(
basic_format_args<buffer_context<Char>> args) { OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using container = remove_reference_t<decltype(internal::get_container(out))>; using container = remove_reference_t<decltype(internal::get_container(out))>;
internal::container_buffer<container> buf((internal::get_container(out))); internal::container_buffer<container> buf((internal::get_container(out)));
internal::vformat_to(buf, to_string_view(format_str), args); internal::vformat_to(buf, to_string_view(format_str), args);
@ -1485,14 +1501,14 @@ template <typename Container, typename S, typename... Args,
inline std::back_insert_iterator<Container> format_to( inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str, std::back_insert_iterator<Container> out, const S& format_str,
Args&&... args) { Args&&... args) {
return vformat_to( return vformat_to(out, to_string_view(format_str),
out, to_string_view(format_str), internal::make_args_checked<Args...>(format_str, args...));
{internal::make_args_checked<Args...>(format_str, args...)});
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const S& format_str, basic_format_args<buffer_context<Char>> args) { const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(to_string_view(format_str), args); return internal::vformat(to_string_view(format_str), args);
} }
@ -1512,7 +1528,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const S& format_str, Args&&... args) { inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
return internal::vformat( return internal::vformat(
to_string_view(format_str), to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)}); internal::make_args_checked<Args...>(format_str, args...));
} }
FMT_API void vprint(string_view, format_args); FMT_API void vprint(string_view, format_args);

View File

@ -8,8 +8,6 @@
#ifndef FMT_FORMAT_INL_H_ #ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_
#include "format.h"
#include <cassert> #include <cassert>
#include <cctype> #include <cctype>
#include <climits> #include <climits>
@ -17,6 +15,8 @@
#include <cstdarg> #include <cstdarg>
#include <cstring> // for std::memmove #include <cstring> // for std::memmove
#include <cwchar> #include <cwchar>
#include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale> # include <locale>
#endif #endif
@ -386,7 +386,8 @@ class fp {
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d); auto u = bit_cast<uint64_t>(d);
f = u & significand_mask; f = u & significand_mask;
auto biased_e = (u & exponent_mask) >> double_significand_size; int biased_e =
static_cast<int>((u & exponent_mask) >> double_significand_size);
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than // Predecessor is closer if d is a normalized power of 2 (f == 0) other than
// the smallest normalized number (biased_e > 1). // the smallest normalized number (biased_e > 1).
bool is_predecessor_closer = f == 0 && biased_e > 1; bool is_predecessor_closer = f == 0 && biased_e > 1;
@ -394,7 +395,7 @@ class fp {
f += implicit_bit; f += implicit_bit;
else else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent). biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
e = static_cast<int>(biased_e - exponent_bias - double_significand_size); e = biased_e - exponent_bias - double_significand_size;
return is_predecessor_closer; return is_predecessor_closer;
} }
@ -458,13 +459,11 @@ inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its // Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. // (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
inline fp get_cached_power(int min_exponent, int& pow10_exponent) { inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
int index = static_cast<int>( int index = static_cast<int>(
static_cast<int64_t>( ((min_exponent + fp::significand_size - 1) * one_over_log2_10 +
(min_exponent + fp::significand_size - 1) * one_over_log2_10 + ((int64_t(1) << 32) - 1)) // ceil
((uint64_t(1) << 32) - 1) // ceil >> 32 // arithmetic shift
) >>
32 // arithmetic shift
); );
// Decimal exponent of the first (smallest) cached power of 10. // Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348; const int first_dec_exp = -348;
@ -1038,7 +1037,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise. // if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template <typename T> template <typename T>
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) { int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
static_assert(!std::is_same<T, float>(), ""); static_assert(!std::is_same<T, float>::value, "");
FMT_ASSERT(value >= 0, "value is negative"); FMT_ASSERT(value >= 0, "value is negative");
const bool fixed = specs.format == float_format::fixed; const bool fixed = specs.format == float_format::fixed;
@ -1113,7 +1112,7 @@ int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf) { buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
static_assert(!std::is_same<T, float>(), ""); static_assert(!std::is_same<T, float>::value, "");
// Subtract 1 to account for the difference in precision since we use %e for // Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format. // both general and exponent format.
@ -1148,7 +1147,8 @@ int snprintf_float(T value, int precision, float_specs specs,
"fuzz mode - avoid large allocation inside snprintf"); "fuzz mode - avoid large allocation inside snprintf");
#endif #endif
// Suppress the warning about a nonliteral format string. // Suppress the warning about a nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF; // Cannot use auto becase of a bug in MinGW (#1532).
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
int result = precision >= 0 int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value) ? snprintf_ptr(begin, capacity, format, precision, value)
: snprintf_ptr(begin, capacity, format, value); : snprintf_ptr(begin, capacity, format, value);
@ -1380,7 +1380,8 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str, FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) { format_args args) {
memory_buffer buffer; memory_buffer buffer;
vformat_to(buffer, format_str, basic_format_args<buffer_context<char>>(args)); internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f); fwrite_fully(buffer.data(), 1, buffer.size(), f);
} }
#endif #endif

View File

@ -33,8 +33,6 @@
#ifndef FMT_FORMAT_H_ #ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_ #define FMT_FORMAT_H_
#include "core.h"
#include <algorithm> #include <algorithm>
#include <cerrno> #include <cerrno>
#include <cmath> #include <cmath>
@ -43,6 +41,8 @@
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include "core.h"
#ifdef __clang__ #ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else #else
@ -221,7 +221,7 @@ namespace internal {
// A helper function to suppress bogus "conditional expression is constant" // A helper function to suppress bogus "conditional expression is constant"
// warnings. // warnings.
template <typename T> inline T const_check(T value) { return value; } template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have // An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
// undefined behavior (e.g. due to type aliasing). // undefined behavior (e.g. due to type aliasing).
@ -235,7 +235,7 @@ inline Dest bit_cast(const Source& source) {
} }
inline bool is_big_endian() { inline bool is_big_endian() {
auto u = 1u; const auto u = 1u;
struct bytes { struct bytes {
char data[sizeof(u)]; char data[sizeof(u)];
}; };
@ -878,11 +878,11 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
return end; return end;
} }
template <typename Int> constexpr int digits10() noexcept { template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
return std::numeric_limits<Int>::digits10; return std::numeric_limits<Int>::digits10;
} }
template <> constexpr int digits10<int128_t>() noexcept { return 38; } template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
template <> constexpr int digits10<uint128_t>() noexcept { return 38; } template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
template <typename Char, typename UInt, typename Iterator, typename F> template <typename Char, typename UInt, typename Iterator, typename F>
inline Iterator format_decimal(Iterator out, UInt value, int num_digits, inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
@ -962,9 +962,24 @@ template <typename T = void> struct null {};
// Workaround an array initialization issue in gcc 4.8. // Workaround an array initialization issue in gcc 4.8.
template <typename Char> struct fill_t { template <typename Char> struct fill_t {
private: private:
Char data_[6]; enum { max_size = 4 };
Char data_[max_size];
unsigned char size_;
public: public:
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
auto size = s.size();
if (size > max_size) {
FMT_THROW(format_error("invalid fill"));
return;
}
for (size_t i = 0; i < size; ++i) data_[i] = s[i];
size_ = static_cast<unsigned char>(size);
}
size_t size() const { return size_; }
const Char* data() const { return data_; }
FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
FMT_CONSTEXPR const Char& operator[](size_t index) const { FMT_CONSTEXPR const Char& operator[](size_t index) const {
return data_[index]; return data_[index];
@ -973,6 +988,7 @@ template <typename Char> struct fill_t {
static FMT_CONSTEXPR fill_t<Char> make() { static FMT_CONSTEXPR fill_t<Char> make() {
auto fill = fill_t<Char>(); auto fill = fill_t<Char>();
fill[0] = Char(' '); fill[0] = Char(' ');
fill.size_ = 1;
return fill; return fill;
} }
}; };
@ -1352,6 +1368,14 @@ template <typename Char> struct nonfinite_writer {
} }
}; };
template <typename OutputIt, typename Char>
OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
auto fill_size = fill.size();
if (fill_size == 1) return std::fill_n(it, n, fill[0]);
for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it);
return it;
}
// This template provides operations for formatting and writing data into a // This template provides operations for formatting and writing data into a
// character range. // character range.
template <typename Range> class basic_writer { template <typename Range> class basic_writer {
@ -1614,20 +1638,20 @@ template <typename Range> class basic_writer {
size_t size = f.size(); // The number of code units. size_t size = f.size(); // The number of code units.
size_t num_code_points = width != 0 ? f.width() : size; size_t num_code_points = width != 0 ? f.width() : size;
if (width <= num_code_points) return f(reserve(size)); if (width <= num_code_points) return f(reserve(size));
auto&& it = reserve(width + (size - num_code_points)); size_t padding = width - num_code_points;
char_type fill = specs.fill[0]; size_t fill_size = specs.fill.size();
std::size_t padding = width - num_code_points; auto&& it = reserve(size + padding * fill_size);
if (specs.align == align::right) { if (specs.align == align::right) {
it = std::fill_n(it, padding, fill); it = fill(it, padding, specs.fill);
f(it); f(it);
} else if (specs.align == align::center) { } else if (specs.align == align::center) {
std::size_t left_padding = padding / 2; std::size_t left_padding = padding / 2;
it = std::fill_n(it, left_padding, fill); it = fill(it, left_padding, specs.fill);
f(it); f(it);
it = std::fill_n(it, padding - left_padding, fill); it = fill(it, padding - left_padding, specs.fill);
} else { } else {
f(it); f(it);
it = std::fill_n(it, padding, fill); it = fill(it, padding, specs.fill);
} }
} }
@ -2008,7 +2032,9 @@ template <typename Char> class specs_setter {
: specs_(other.specs_) {} : specs_(other.specs_) {}
FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill[0] = fill; } FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
specs_.fill = fill;
}
FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; }
FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
@ -2320,16 +2346,25 @@ template <typename SpecHandler, typename Char> struct precision_adapter {
SpecHandler& handler; SpecHandler& handler;
}; };
template <typename Char>
FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) {
if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1;
do {
++begin;
} while (begin != end && (*begin & 0xc0) == 0x80);
return begin;
}
// Parses fill and alignment. // Parses fill and alignment.
template <typename Char, typename Handler> template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
Handler&& handler) { Handler&& handler) {
FMT_ASSERT(begin != end, ""); FMT_ASSERT(begin != end, "");
auto align = align::none; auto align = align::none;
int i = 0; auto p = next_code_point(begin, end);
if (begin + 1 != end) ++i; if (p == end) p = begin;
do { for (;;) {
switch (static_cast<char>(begin[i])) { switch (static_cast<char>(*p)) {
case '<': case '<':
align = align::left; align = align::left;
break; break;
@ -2346,18 +2381,21 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
break; break;
} }
if (align != align::none) { if (align != align::none) {
if (i > 0) { if (p != begin) {
auto c = *begin; auto c = *begin;
if (c == '{') if (c == '{')
return handler.on_error("invalid fill character '{'"), begin; return handler.on_error("invalid fill character '{'"), begin;
begin += 2; handler.on_fill(basic_string_view<Char>(begin, p - begin));
handler.on_fill(c); begin = p + 1;
} else } else
++begin; ++begin;
handler.on_align(align); handler.on_align(align);
break; break;
} else if (p == begin) {
break;
}
p = begin;
} }
} while (i-- > 0);
return begin; return begin;
} }
@ -3178,6 +3216,11 @@ arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
std::vector<int> v = {1, 2, 3}; std::vector<int> v = {1, 2, 3};
fmt::print("{}", fmt::join(v, ", ")); fmt::print("{}", fmt::join(v, ", "));
// Output: "1, 2, 3" // Output: "1, 2, 3"
``fmt::join`` applies passed format specifiers to the range elements::
fmt::print("{:02}", fmt::join(v, ", "));
// Output: "01, 02, 03"
\endrst \endrst
*/ */
template <typename Range> template <typename Range>
@ -3223,7 +3266,7 @@ std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
template <typename Char> template <typename Char>
typename buffer_context<Char>::iterator internal::vformat_to( typename buffer_context<Char>::iterator internal::vformat_to(
internal::buffer<Char>& buf, basic_string_view<Char> format_str, internal::buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>; using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str),
args); args);
@ -3233,7 +3276,7 @@ template <typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(internal::is_string<S>::value)> FMT_ENABLE_IF(internal::is_string<S>::value)>
inline typename buffer_context<Char>::iterator vformat_to( inline typename buffer_context<Char>::iterator vformat_to(
internal::buffer<Char>& buf, const S& format_str, internal::buffer<Char>& buf, const S& format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat_to(buf, to_string_view(format_str), args); return internal::vformat_to(buf, to_string_view(format_str), args);
} }
@ -3244,7 +3287,7 @@ inline typename buffer_context<Char>::iterator format_to(
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
using context = buffer_context<Char>; using context = buffer_context<Char>;
return internal::vformat_to(buf, to_string_view(format_str), return internal::vformat_to(buf, to_string_view(format_str),
{make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
template <typename OutputIt, typename Char = char> template <typename OutputIt, typename Char = char>
@ -3257,8 +3300,9 @@ template <typename S, typename OutputIt, typename... Args,
FMT_ENABLE_IF( FMT_ENABLE_IF(
internal::is_output_iterator<OutputIt>::value && internal::is_output_iterator<OutputIt>::value &&
!internal::is_contiguous_back_insert_iterator<OutputIt>::value)> !internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
inline OutputIt vformat_to(OutputIt out, const S& format_str, inline OutputIt vformat_to(
format_args_t<OutputIt, char_t<S>> args) { OutputIt out, const S& format_str,
format_args_t<type_identity_t<OutputIt>, char_t<S>> args) {
using range = internal::output_range<OutputIt, char_t<S>>; using range = internal::output_range<OutputIt, char_t<S>>;
return vformat_to<arg_formatter<range>>(range(out), return vformat_to<arg_formatter<range>>(range(out),
to_string_view(format_str), args); to_string_view(format_str), args);
@ -3284,7 +3328,7 @@ inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>; using context = format_context_t<OutputIt, char_t<S>>;
return vformat_to(out, to_string_view(format_str), return vformat_to(out, to_string_view(format_str),
{make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
template <typename OutputIt> struct format_to_n_result { template <typename OutputIt> struct format_to_n_result {
@ -3312,7 +3356,7 @@ template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)> FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
inline format_to_n_result<OutputIt> vformat_to_n( inline format_to_n_result<OutputIt> vformat_to_n(
OutputIt out, std::size_t n, basic_string_view<Char> format_str, OutputIt out, std::size_t n, basic_string_view<Char> format_str,
format_to_n_args<OutputIt, Char> args) { format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n), auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
format_str, args); format_str, args);
return {it.base(), it.count()}; return {it.base(), it.count()};
@ -3334,13 +3378,13 @@ inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n,
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
using context = format_to_n_context<OutputIt, char_t<S>>; using context = format_to_n_context<OutputIt, char_t<S>>;
return vformat_to_n(out, n, to_string_view(format_str), return vformat_to_n(out, n, to_string_view(format_str),
{make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
template <typename Char> template <typename Char>
inline std::basic_string<Char> internal::vformat( inline std::basic_string<Char> internal::vformat(
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
return to_string(buffer); return to_string(buffer);
@ -3405,6 +3449,10 @@ template <typename Char> struct udl_arg {
} }
}; };
template <typename Char, size_t N>
FMT_CONSTEXPR basic_string_view<Char> literal_to_view(const Char (&s)[N]) {
return {s, N - 1};
}
} // namespace internal } // namespace internal
inline namespace literals { inline namespace literals {
@ -3468,7 +3516,8 @@ FMT_END_NAMESPACE
using char_type = fmt::remove_cvref_t<decltype(*s)>; \ using char_type = fmt::remove_cvref_t<decltype(*s)>; \
__VA_ARGS__ FMT_CONSTEXPR \ __VA_ARGS__ FMT_CONSTEXPR \
operator fmt::basic_string_view<char_type>() const { \ operator fmt::basic_string_view<char_type>() const { \
return {s, sizeof(s) / sizeof(char_type) - 1}; \ /* FMT_STRING only accepts string literals. */ \
return fmt::internal::literal_to_view(s); \
} \ } \
}; \ }; \
return FMT_STRING(); \ return FMT_STRING(); \
@ -3476,7 +3525,7 @@ FMT_END_NAMESPACE
/** /**
\rst \rst
Constructs a compile-time format string. Constructs a compile-time format string from a string literal *s*.
**Example**:: **Example**::

View File

@ -9,6 +9,7 @@
#define FMT_LOCALE_H_ #define FMT_LOCALE_H_
#include <locale> #include <locale>
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -18,16 +19,16 @@ template <typename Char>
typename buffer_context<Char>::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf, const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>; using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args, return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc)); internal::locale_ref(loc));
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc, std::basic_string<Char> vformat(
basic_string_view<Char> format_str, const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer); return fmt::to_string(buffer);
@ -37,7 +38,7 @@ std::basic_string<Char> vformat(const std::locale& loc,
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args); return internal::vformat(loc, to_string_view(format_str), args);
} }
@ -46,15 +47,15 @@ inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
return internal::vformat( return internal::vformat(
loc, to_string_view(format_str), loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)}); internal::make_args_checked<Args...>(format_str, args...));
} }
template <typename S, typename OutputIt, typename... Args, template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t< typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>> internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc, inline OutputIt vformat_to(
const S& format_str, OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<OutputIt, Char> args) { format_args_t<type_identity_t<OutputIt>, Char> args) {
using range = internal::output_range<OutputIt, Char>; using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc)); range(out), to_string_view(format_str), args, internal::locale_ref(loc));

View File

@ -93,7 +93,9 @@ void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) { locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf); formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>()); if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
buf.resize(buf.size()); buf.resize(buf.size());
@ -115,7 +117,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
@ -134,7 +136,7 @@ template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str), vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)}); internal::make_args_checked<Args...>(format_str, args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -597,7 +597,8 @@ inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf( inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) { const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
return to_string(buffer); return to_string(buffer);
@ -616,12 +617,13 @@ template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)}); return vsprintf(to_string_view(format), make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format, inline int vfprintf(
basic_format_args<basic_printf_context_t<Char>> args) { std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size(); std::size_t size = buffer.size();
@ -644,12 +646,13 @@ template <typename S, typename... Args,
inline int fprintf(std::FILE* f, const S& format, const Args&... args) { inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vprintf(const S& format, inline int vprintf(
basic_format_args<basic_printf_context_t<Char>> args) { const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args); return vfprintf(stdout, to_string_view(format), args);
} }
@ -667,12 +670,13 @@ template <typename S, typename... Args,
inline int printf(const S& format_str, const Args&... args) { inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>; using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str), return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format, inline int vfprintf(
basic_format_args<basic_printf_context_t<Char>> args) { std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
internal::write(os, buffer); internal::write(os, buffer);
@ -683,9 +687,9 @@ inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
template <typename ArgFormatter, typename Char, template <typename ArgFormatter, typename Char,
typename Context = typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>> basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out, typename ArgFormatter::iterator vprintf(
basic_string_view<Char> format_str, internal::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<Context> args) { basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out); typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>(); Context(iter, format_str, args).template format<ArgFormatter>();
return iter; return iter;
@ -705,7 +709,7 @@ inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) { const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str), return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)}); make_format_args<context>(args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -19,7 +19,7 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
"fuzz mode - avoid large allocation inside snprintf"); "fuzz mode - avoid large allocation inside snprintf");
#endif #endif
// Suppress the warning about nonliteral format string. // Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF; int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value) return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value); : snprintf_ptr(buf, size, format, precision, value);
} }

View File

@ -145,6 +145,48 @@ TEST(ChronoTest, FormatDefault) {
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42))); fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
} }
TEST(ChronoTest, FormatWide) {
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
EXPECT_EQ(L"42as",
fmt::format(L"{}", std::chrono::duration<int, std::atto>(42)));
EXPECT_EQ(L"42fs",
fmt::format(L"{}", std::chrono::duration<int, std::femto>(42)));
EXPECT_EQ(L"42ps",
fmt::format(L"{}", std::chrono::duration<int, std::pico>(42)));
EXPECT_EQ(L"42ns", fmt::format(L"{}", std::chrono::nanoseconds(42)));
EXPECT_EQ(L"42\u00B5s", fmt::format(L"{}", std::chrono::microseconds(42)));
EXPECT_EQ(L"42ms", fmt::format(L"{}", std::chrono::milliseconds(42)));
EXPECT_EQ(L"42cs",
fmt::format(L"{}", std::chrono::duration<int, std::centi>(42)));
EXPECT_EQ(L"42ds",
fmt::format(L"{}", std::chrono::duration<int, std::deci>(42)));
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
EXPECT_EQ(L"42das",
fmt::format(L"{}", std::chrono::duration<int, std::deca>(42)));
EXPECT_EQ(L"42hs",
fmt::format(L"{}", std::chrono::duration<int, std::hecto>(42)));
EXPECT_EQ(L"42ks",
fmt::format(L"{}", std::chrono::duration<int, std::kilo>(42)));
EXPECT_EQ(L"42Ms",
fmt::format(L"{}", std::chrono::duration<int, std::mega>(42)));
EXPECT_EQ(L"42Gs",
fmt::format(L"{}", std::chrono::duration<int, std::giga>(42)));
EXPECT_EQ(L"42Ts",
fmt::format(L"{}", std::chrono::duration<int, std::tera>(42)));
EXPECT_EQ(L"42Ps",
fmt::format(L"{}", std::chrono::duration<int, std::peta>(42)));
EXPECT_EQ(L"42Es",
fmt::format(L"{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ(L"42m", fmt::format(L"{}", std::chrono::minutes(42)));
EXPECT_EQ(L"42h", fmt::format(L"{}", std::chrono::hours(42)));
EXPECT_EQ(
L"42[15]s",
fmt::format(L"{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ(
L"42[15/4]s",
fmt::format(L"{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
}
TEST(ChronoTest, Align) { TEST(ChronoTest, Align) {
auto s = std::chrono::seconds(42); auto s = std::chrono::seconds(42);
EXPECT_EQ("42s ", fmt::format("{:5}", s)); EXPECT_EQ("42s ", fmt::format("{:5}", s));

View File

@ -83,9 +83,9 @@ namespace std {
Out format_to(Out out, wstring_view fmt, const Args&... args); Out format_to(Out out, wstring_view fmt, const Args&... args);
template<class Out> template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args); Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
template<class Out> template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args); Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out> template<class Out>
struct format_to_n_result { struct format_to_n_result {
@ -730,17 +730,17 @@ wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args> template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args) { Out format_to(Out out, string_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>; using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, {make_format_args<context>(args...)}); return vformat_to(out, fmt, make_format_args<context>(args...));
} }
template<class Out, class... Args> template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args) { Out format_to(Out out, wstring_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>; using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, {make_format_args<context>(args...)}); return vformat_to(out, fmt, make_format_args<context>(args...));
} }
template<class Out> template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<Out, char> args) { Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using range = fmt::internal::output_range<Out, char>; using range = fmt::internal::output_range<Out, char>;
detail::format_handler<detail::arg_formatter<range>, char, basic_format_context<Out, char>> detail::format_handler<detail::arg_formatter<range>, char, basic_format_context<Out, char>>
h(range(out), fmt, args, {}); h(range(out), fmt, args, {});
@ -749,7 +749,7 @@ template<class Out>
} }
template<class Out> template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<Out, wchar_t> args); Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out, class... Args> template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n, format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,

View File

@ -815,6 +815,9 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("foo=", format("{:}=", "foo")); EXPECT_EQ("foo=", format("{:}=", "foo"));
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*')); EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
EXPECT_EQ("жж42", format("{0:ж>4}", 42));
EXPECT_THROW_MSG(format("{:\x80\x80\x80\x80\x80>}", 0), format_error,
"invalid fill");
} }
TEST(FormatterTest, PlusSign) { TEST(FormatterTest, PlusSign) {
@ -2173,7 +2176,7 @@ struct test_format_specs_handler {
type(other.type) {} type(other.type) {}
FMT_CONSTEXPR void on_align(fmt::align_t a) { align = a; } FMT_CONSTEXPR void on_align(fmt::align_t a) { align = a; }
FMT_CONSTEXPR void on_fill(char f) { fill = f; } FMT_CONSTEXPR void on_fill(fmt::string_view f) { fill = f[0]; }
FMT_CONSTEXPR void on_plus() { res = PLUS; } FMT_CONSTEXPR void on_plus() { res = PLUS; }
FMT_CONSTEXPR void on_minus() { res = MINUS; } FMT_CONSTEXPR void on_minus() { res = MINUS; }
FMT_CONSTEXPR void on_space() { res = SPACE; } FMT_CONSTEXPR void on_space() { res = SPACE; }