From 65ac626c5856f5aad1f1542e79407a6714357043 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 12 Jan 2020 07:26:16 -0800 Subject: [PATCH 01/31] Improve join docs --- include/fmt/format.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/fmt/format.h b/include/fmt/format.h index ee8ab92f..50cf37c9 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3178,6 +3178,11 @@ arg_join join(It begin, It end, wstring_view sep) { std::vector v = {1, 2, 3}; fmt::print("{}", fmt::join(v, ", ")); // 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 */ template From 77165fdf85ebb9fcf11c505e38ee9e8d0687bdb6 Mon Sep 17 00:00:00 2001 From: Tobias Hammer Date: Mon, 13 Jan 2020 18:18:44 +0100 Subject: [PATCH 02/31] Use FMT_NOEXCEPT instead of noexcept directly Otherwise breaks on compilers without noexcept support --- include/fmt/format.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 50cf37c9..af57862b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -878,11 +878,11 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits, return end; } -template constexpr int digits10() noexcept { +template constexpr int digits10() FMT_NOEXCEPT { return std::numeric_limits::digits10; } -template <> constexpr int digits10() noexcept { return 38; } -template <> constexpr int digits10() noexcept { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } template inline Iterator format_decimal(Iterator out, UInt value, int num_digits, From ae3ea156eae35e8a6e010a73229de1b128466582 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 14 Jan 2020 08:50:17 -0700 Subject: [PATCH 03/31] Fix for older versions of intel compiler The intel-17 and intel-18 compilers seem to require that `u` be `const`: ``` /src/fmt/format.h(226): warning #437: reference to local variable of enclosing function is not allowed char data[sizeof(u)]; ``` If `u` is declared as `const auto u =1u` instead of just `auto u=1u`, the file compiles with no warnings. --- include/fmt/format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index af57862b..38626caf 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -235,7 +235,7 @@ inline Dest bit_cast(const Source& source) { } inline bool is_big_endian() { - auto u = 1u; + const auto u = 1u; struct bytes { char data[sizeof(u)]; }; From 55b6130055aa0bea93c76d6a717847fa81cfb6f7 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 14 Jan 2020 11:10:11 -0700 Subject: [PATCH 04/31] Use C++11-compatible operations The `std::is_base_of()` and `std::is_reference()` member functions were added in C++14. To maintain C++11 compatibility, use the `::value` instead. Current code fails on intel-17 and other compilers if using strict C++11 --- include/fmt/core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index eadd4ab1..44cc3d95 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1426,8 +1426,8 @@ template > inline format_arg_store, remove_reference_t...> make_args_checked(const S& format_str, const remove_reference_t&... args) { - static_assert(all_true<(!std::is_base_of>() || - !std::is_reference())...>::value, + static_assert(all_true<(!std::is_base_of>::value || + !std::is_reference::value)...>::value, "passing views as lvalues is disallowed"); check_format_string>...>(format_str); return {args...}; From 4bbe57cebf0db8ec9fe6c2f57d3d068cf1465f19 Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 14 Jan 2020 11:50:57 -0700 Subject: [PATCH 05/31] Work-around for nvcc The nvcc compiler (at least up to 9.2) defines `__SIZEOF_INT128__`, but doesn't support 128-bit integers on device code: ``` error: "fmt::v6::format_arg_store>, char>, const char *, int, const char *>" contains a 128-bit integer, which is not supported in device code ``` --- include/fmt/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 44cc3d95..c2f997f7 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -266,7 +266,7 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC # define FMT_USE_INT128 1 using int128_t = __int128_t; using uint128_t = __uint128_t; From c8dd9cc99da8464fce62dfd1e4304c9e74587a47 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 15 Jan 2020 08:09:48 -0800 Subject: [PATCH 06/31] Use type_identity to block unnecessary template argument deduction (thanks Tim Song) --- include/fmt/color.h | 6 ++---- include/fmt/compile.h | 7 ++++--- include/fmt/core.h | 28 +++++++++++++++++++++------- include/fmt/format-inl.h | 7 ++++--- include/fmt/format.h | 19 ++++++++++--------- include/fmt/printf.h | 32 ++++++++++++++++++-------------- test/format | 12 ++++++------ 7 files changed, 65 insertions(+), 46 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 362a95e1..f101c892 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer& buf, const text_style& ts, internal::make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - vformat_to(buf, format_str, args); - if (has_style) { - internal::reset_color(buf); - } + internal::vformat_to(buf, format_str, args); + if (has_style) internal::reset_color(buf); } } // namespace internal diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 5829f623..2e166b5c 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -9,6 +9,7 @@ #define FMT_COMPILE_H_ #include + #include "format.h" FMT_BEGIN_NAMESPACE @@ -549,7 +550,7 @@ std::basic_string format(const CompiledFormat& cf, const Args&... args) { using range = buffer_range; using context = buffer_context; internal::cf::vformat_to(range(buffer), cf, - {make_format_args(args...)}); + make_format_args(args...)); return to_string(buffer); } @@ -561,8 +562,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf, using char_type = typename CompiledFormat::char_type; using range = internal::output_range; using context = format_context_t; - return internal::cf::vformat_to( - range(out), cf, {make_format_args(args...)}); + return internal::cf::vformat_to(range(out), cf, + make_format_args(args...)); } template using enable_if_t = typename std::enable_if::type; template @@ -229,6 +229,8 @@ template using remove_const_t = typename std::remove_const::type; template using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; struct monostate {}; @@ -1220,7 +1222,13 @@ using wformat_context = buffer_context; such as `~fmt::vformat`. \endrst */ -template class format_arg_store { +template +class format_arg_store +#if FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ private: static const size_t num_args = sizeof...(Args); static const bool is_packed = num_args < internal::max_packed_args; @@ -1239,7 +1247,12 @@ template class format_arg_store { : internal::is_unpacked_bit | num_args; format_arg_store(const Args&... args) - : data_{internal::make_arg(args)...} {} + : +#if FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{internal::make_arg(args)...} { + } }; /** @@ -1426,9 +1439,10 @@ template > inline format_arg_store, remove_reference_t...> make_args_checked(const S& format_str, const remove_reference_t&... args) { - static_assert(all_true<(!std::is_base_of>::value || - !std::is_reference::value)...>::value, - "passing views as lvalues is disallowed"); + static_assert( + all_true<(!std::is_base_of>::value || + !std::is_reference::value)...>::value, + "passing views as lvalues is disallowed"); check_format_string>...>(format_str); return {args...}; } @@ -1440,7 +1454,7 @@ std::basic_string vformat(basic_string_view format_str, template typename buffer_context::iterator vformat_to( buffer& buf, basic_string_view format_str, - basic_format_args> args); + basic_format_args>> args); FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); } // namespace internal diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 117212e9..5f07a465 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -8,8 +8,6 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include "format.h" - #include #include #include @@ -17,6 +15,8 @@ #include #include // for std::memmove #include + +#include "format.h" #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) # include #endif @@ -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, format_args args) { memory_buffer buffer; - vformat_to(buffer, format_str, basic_format_args>(args)); + internal::vformat_to(buffer, format_str, + basic_format_args>(args)); fwrite_fully(buffer.data(), 1, buffer.size(), f); } #endif diff --git a/include/fmt/format.h b/include/fmt/format.h index 38626caf..7552a1b5 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -33,8 +33,6 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include "core.h" - #include #include #include @@ -43,6 +41,8 @@ #include #include +#include "core.h" + #ifdef __clang__ # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else @@ -3228,7 +3228,7 @@ std::basic_string to_string(const basic_memory_buffer& buf) { template typename buffer_context::iterator internal::vformat_to( internal::buffer& buf, basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { using range = buffer_range; return vformat_to>(buf, to_string_view(format_str), args); @@ -3238,7 +3238,7 @@ template , FMT_ENABLE_IF(internal::is_string::value)> inline typename buffer_context::iterator vformat_to( internal::buffer& buf, const S& format_str, - basic_format_args> args) { + basic_format_args>> args) { return internal::vformat_to(buf, to_string_view(format_str), args); } @@ -3262,8 +3262,9 @@ template ::value && !internal::is_contiguous_back_insert_iterator::value)> -inline OutputIt vformat_to(OutputIt out, const S& format_str, - format_args_t> args) { +inline OutputIt vformat_to( + OutputIt out, const S& format_str, + format_args_t, char_t> args) { using range = internal::output_range>; return vformat_to>(range(out), to_string_view(format_str), args); @@ -3289,7 +3290,7 @@ inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) { internal::check_format_string(format_str); using context = format_context_t>; return vformat_to(out, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template struct format_to_n_result { @@ -3317,7 +3318,7 @@ template ::value)> inline format_to_n_result vformat_to_n( OutputIt out, std::size_t n, basic_string_view format_str, - format_to_n_args args) { + format_to_n_args, type_identity_t> args) { auto it = vformat_to(internal::truncating_iterator(out, n), format_str, args); return {it.base(), it.count()}; @@ -3339,7 +3340,7 @@ inline format_to_n_result format_to_n(OutputIt out, std::size_t n, internal::check_format_string(format_str); using context = format_to_n_context>; return vformat_to_n(out, n, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 047aa323..8a2a8c20 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -597,7 +597,8 @@ inline format_arg_store make_wprintf_args( template > inline std::basic_string vsprintf( - const S& format, basic_format_args> args) { + const S& format, + basic_format_args>> args) { basic_memory_buffer buffer; printf(buffer, to_string_view(format), args); return to_string(buffer); @@ -616,12 +617,13 @@ template ::value, char_t>> inline std::basic_string sprintf(const S& format, const Args&... args) { using context = basic_printf_context_t; - return vsprintf(to_string_view(format), {make_format_args(args...)}); + return vsprintf(to_string_view(format), make_format_args(args...)); } template > -inline int vfprintf(std::FILE* f, const S& format, - basic_format_args> args) { +inline int vfprintf( + std::FILE* f, const S& format, + basic_format_args>> args) { basic_memory_buffer buffer; printf(buffer, to_string_view(format), args); std::size_t size = buffer.size(); @@ -644,12 +646,13 @@ template ; return vfprintf(f, to_string_view(format), - {make_format_args(args...)}); + make_format_args(args...)); } template > -inline int vprintf(const S& format, - basic_format_args> args) { +inline int vprintf( + const S& format, + basic_format_args>> args) { return vfprintf(stdout, to_string_view(format), args); } @@ -667,12 +670,13 @@ template >; return vprintf(to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template > -inline int vfprintf(std::basic_ostream& os, const S& format, - basic_format_args> args) { +inline int vfprintf( + std::basic_ostream& os, const S& format, + basic_format_args>> args) { basic_memory_buffer buffer; printf(buffer, to_string_view(format), args); internal::write(os, buffer); @@ -683,9 +687,9 @@ inline int vfprintf(std::basic_ostream& os, const S& format, template > -typename ArgFormatter::iterator vprintf(internal::buffer& out, - basic_string_view format_str, - basic_format_args args) { +typename ArgFormatter::iterator vprintf( + internal::buffer& out, basic_string_view format_str, + basic_format_args> args) { typename ArgFormatter::iterator iter(out); Context(iter, format_str, args).template format(); return iter; @@ -705,7 +709,7 @@ inline int fprintf(std::basic_ostream& os, const S& format_str, const Args&... args) { using context = basic_printf_context_t; return vfprintf(os, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } FMT_END_NAMESPACE diff --git a/test/format b/test/format index ea26a005..62fa3167 100644 --- a/test/format +++ b/test/format @@ -83,9 +83,9 @@ namespace std { Out format_to(Out out, wstring_view fmt, const Args&... args); template - Out vformat_to(Out out, string_view fmt, format_args_t args); + Out vformat_to(Out out, string_view fmt, format_args_t, char> args); template - Out vformat_to(Out out, wstring_view fmt, format_args_t args); + Out vformat_to(Out out, wstring_view fmt, format_args_t, wchar_t> args); template struct format_to_n_result { @@ -730,17 +730,17 @@ wstring vformat(wstring_view fmt, wformat_args args); template Out format_to(Out out, string_view fmt, const Args&... args) { using context = basic_format_context; - return vformat_to(out, fmt, {make_format_args(args...)}); + return vformat_to(out, fmt, make_format_args(args...)); } template Out format_to(Out out, wstring_view fmt, const Args&... args) { using context = basic_format_context; - return vformat_to(out, fmt, {make_format_args(args...)}); + return vformat_to(out, fmt, make_format_args(args...)); } template - Out vformat_to(Out out, string_view fmt, format_args_t args) { + Out vformat_to(Out out, string_view fmt, format_args_t, char> args) { using range = fmt::internal::output_range; detail::format_handler, char, basic_format_context> h(range(out), fmt, args, {}); @@ -749,7 +749,7 @@ template } template - Out vformat_to(Out out, wstring_view fmt, format_args_t args); + Out vformat_to(Out out, wstring_view fmt, format_args_t, wchar_t> args); template format_to_n_result format_to_n(Out out, iter_difference_t n, From 40638a75b3245702272da9c7a9afefb4223cc3af Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 14 Jan 2020 10:54:19 -0700 Subject: [PATCH 07/31] Use C++11 compatible std::is_same operations The `operator()` member function of `std::is_same` was added in C++14. For C++11, the `::value` needs to be used instead. --- include/fmt/format-inl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 5f07a465..d812c3e1 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1038,7 +1038,7 @@ void fallback_format(Double d, buffer& buf, int& exp10) { // if T is a IEEE754 binary32 or binary64 and snprintf otherwise. template int format_float(T value, int precision, float_specs specs, buffer& buf) { - static_assert(!std::is_same(), ""); + static_assert(!std::is_same::value, ""); FMT_ASSERT(value >= 0, "value is negative"); const bool fixed = specs.format == float_format::fixed; @@ -1113,7 +1113,7 @@ int snprintf_float(T value, int precision, float_specs specs, buffer& buf) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - static_assert(!std::is_same(), ""); + static_assert(!std::is_same::value, ""); // Subtract 1 to account for the difference in precision since we use %e for // both general and exponent format. From 4ccbe4b5f29c5ed819620101026e501c7f9bd953 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 15 Jan 2020 08:05:58 -0700 Subject: [PATCH 08/31] Avoid namespace clash for fmt ## Problem In the case of an existing `fmt` namespace (in my project this looks like `Project::fmt`) it is possible to get a namespace clash in debug builds (MSVC 2017) ## Proposed Solution When referencing `fmt` internally, be explicit that it is relative to the global namespace using `::fmt` --- include/fmt/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 129569a0..4a777f75 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -253,7 +253,7 @@ FMT_API void assert_fail(const char* file, int line, const char* message); # define FMT_ASSERT(condition, message) \ ((condition) \ ? void() \ - : fmt::internal::assert_fail(__FILE__, __LINE__, (message))) + : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message))) # endif #endif From 1f110702a1c24c836b2a2a15f7c959d22582a704 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 15 Jan 2020 11:42:59 -0800 Subject: [PATCH 09/31] Remove redundant braces --- include/fmt/color.h | 4 ++-- include/fmt/core.h | 20 +++++++++++--------- include/fmt/format.h | 4 ++-- include/fmt/locale.h | 19 ++++++++++--------- include/fmt/ostream.h | 4 ++-- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index f101c892..3756ba3f 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -538,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) { template > inline std::basic_string vformat( const text_style& ts, const S& format_str, - basic_format_args> args) { + basic_format_args>> args) { basic_memory_buffer buf; internal::vformat_to(buf, ts, to_string_view(format_str), args); return fmt::to_string(buf); @@ -560,7 +560,7 @@ template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { return vformat(ts, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } FMT_END_NAMESPACE diff --git a/include/fmt/core.h b/include/fmt/core.h index 4a777f75..00e2c3a3 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1448,8 +1448,9 @@ make_args_checked(const S& format_str, } template -std::basic_string vformat(basic_string_view format_str, - basic_format_args> args); +std::basic_string vformat( + basic_string_view format_str, + basic_format_args>> args); template typename buffer_context::iterator vformat_to( @@ -1485,8 +1486,9 @@ void arg(S, internal::named_arg) = delete; template , FMT_ENABLE_IF( internal::is_contiguous_back_insert_iterator::value)> -OutputIt vformat_to(OutputIt out, const S& format_str, - basic_format_args> args) { +OutputIt vformat_to( + OutputIt out, const S& format_str, + basic_format_args>> args) { using container = remove_reference_t; internal::container_buffer buf((internal::get_container(out))); internal::vformat_to(buf, to_string_view(format_str), args); @@ -1499,14 +1501,14 @@ template format_to( std::back_insert_iterator out, const S& format_str, Args&&... args) { - return vformat_to( - out, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + return vformat_to(out, to_string_view(format_str), + internal::make_args_checked(format_str, args...)); } template > inline std::basic_string vformat( - const S& format_str, basic_format_args> args) { + const S& format_str, + basic_format_args>> args) { return internal::vformat(to_string_view(format_str), args); } @@ -1526,7 +1528,7 @@ template > inline std::basic_string format(const S& format_str, Args&&... args) { return internal::vformat( to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } FMT_API void vprint(string_view, format_args); diff --git a/include/fmt/format.h b/include/fmt/format.h index 7552a1b5..b6a26f3b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3249,7 +3249,7 @@ inline typename buffer_context::iterator format_to( internal::check_format_string(format_str); using context = buffer_context; return internal::vformat_to(buf, to_string_view(format_str), - {make_format_args(args...)}); + make_format_args(args...)); } template @@ -3346,7 +3346,7 @@ inline format_to_n_result format_to_n(OutputIt out, std::size_t n, template inline std::basic_string internal::vformat( basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { basic_memory_buffer buffer; internal::vformat_to(buffer, format_str, args); return to_string(buffer); diff --git a/include/fmt/locale.h b/include/fmt/locale.h index 7c13656e..70bc1935 100644 --- a/include/fmt/locale.h +++ b/include/fmt/locale.h @@ -9,6 +9,7 @@ #define FMT_LOCALE_H_ #include + #include "format.h" FMT_BEGIN_NAMESPACE @@ -18,16 +19,16 @@ template typename buffer_context::iterator vformat_to( const std::locale& loc, buffer& buf, basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { using range = buffer_range; return vformat_to>(buf, to_string_view(format_str), args, internal::locale_ref(loc)); } template -std::basic_string vformat(const std::locale& loc, - basic_string_view format_str, - basic_format_args> args) { +std::basic_string vformat( + const std::locale& loc, basic_string_view format_str, + basic_format_args>> args) { basic_memory_buffer buffer; internal::vformat_to(loc, buffer, format_str, args); return fmt::to_string(buffer); @@ -37,7 +38,7 @@ std::basic_string vformat(const std::locale& loc, template > inline std::basic_string vformat( const std::locale& loc, const S& format_str, - basic_format_args> args) { + basic_format_args>> args) { return internal::vformat(loc, to_string_view(format_str), args); } @@ -46,15 +47,15 @@ inline std::basic_string format(const std::locale& loc, const S& format_str, Args&&... args) { return internal::vformat( loc, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } template ::value, char_t>> -inline OutputIt vformat_to(OutputIt out, const std::locale& loc, - const S& format_str, - format_args_t args) { +inline OutputIt vformat_to( + OutputIt out, const std::locale& loc, const S& format_str, + format_args_t, Char> args) { using range = internal::output_range; return vformat_to>( range(out), to_string_view(format_str), args, internal::locale_ref(loc)); diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 72d078b2..75a8cd4b 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -115,7 +115,7 @@ struct fallback_formatter::value>> template void vprint(std::basic_ostream& os, basic_string_view format_str, - basic_format_args> args) { + basic_format_args>> args) { basic_memory_buffer buffer; internal::vformat_to(buffer, format_str, args); internal::write(os, buffer); @@ -134,7 +134,7 @@ template ::value, char_t>> void print(std::basic_ostream& os, const S& format_str, Args&&... args) { vprint(os, to_string_view(format_str), - {internal::make_args_checked(format_str, args...)}); + internal::make_args_checked(format_str, args...)); } FMT_END_NAMESPACE From 0f0e5ddf5fc6152ca60f26b0d9236f7ad3afd974 Mon Sep 17 00:00:00 2001 From: JackBoosY Date: Wed, 15 Jan 2020 23:45:41 -0800 Subject: [PATCH 10/31] Add vcpkg installation instructions --- README.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rst b/README.rst index 34427826..60db80cd 100644 --- a/README.rst +++ b/README.rst @@ -236,6 +236,20 @@ Folly Format 79.9 445 430 compare formatting function overhead only. Boost Format is a header-only library so it doesn't provide any linkage options. + +Quick build +~~~~~~~~~~~~~~~~~ + +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. + Running the tests ~~~~~~~~~~~~~~~~~ From ffd5f3469fd7a55dfed3b1fab21fe078d1259e95 Mon Sep 17 00:00:00 2001 From: JackBoosY Date: Wed, 15 Jan 2020 23:52:11 -0800 Subject: [PATCH 11/31] Correct display format --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 60db80cd..d3a9346c 100644 --- a/README.rst +++ b/README.rst @@ -240,13 +240,13 @@ header-only library so it doesn't provide any linkage options. Quick build ~~~~~~~~~~~~~~~~~ -You can download and install fmt using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: +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 + $ 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. From b124e3e8e7a63a4cb192179cb9c6e501cc2f7923 Mon Sep 17 00:00:00 2001 From: JackBoosY Date: Wed, 15 Jan 2020 23:55:49 -0800 Subject: [PATCH 12/31] fix url link --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d3a9346c..a2e6076b 100644 --- a/README.rst +++ b/README.rst @@ -240,7 +240,7 @@ header-only library so it doesn't provide any linkage options. Quick build ~~~~~~~~~~~~~~~~~ -You can download and install fmt using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:: +You can download and install fmt using the `vcpkg`_ dependency manager:: $ git clone https://github.com/Microsoft/vcpkg.git $ cd vcpkg @@ -248,7 +248,7 @@ You can download and install fmt using the [vcpkg](https://github.com/Microsoft/ $ ./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. +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`_ on the vcpkg repository. Running the tests ~~~~~~~~~~~~~~~~~ From 11cc2903e4b53d10b092cfd57e28015168b1b757 Mon Sep 17 00:00:00 2001 From: JackBoosY Date: Wed, 15 Jan 2020 23:59:52 -0800 Subject: [PATCH 13/31] re-fix url link --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a2e6076b..acb876ee 100644 --- a/README.rst +++ b/README.rst @@ -240,7 +240,7 @@ header-only library so it doesn't provide any linkage options. Quick build ~~~~~~~~~~~~~~~~~ -You can download and install fmt using the `vcpkg`_ dependency manager:: +You can download and install fmt using the `vcpkg `_ dependency manager:: $ git clone https://github.com/Microsoft/vcpkg.git $ cd vcpkg @@ -248,7 +248,7 @@ You can download and install fmt using the `vcpkg`_ on the vcpkg repository. +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 `_ on the vcpkg repository. Running the tests ~~~~~~~~~~~~~~~~~ From 1bd4f54fa627ce358b44984fe15ef1a0446ccfc4 Mon Sep 17 00:00:00 2001 From: JackBoosY Date: Thu, 16 Jan 2020 18:53:51 -0800 Subject: [PATCH 14/31] update format --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index acb876ee..19ea3a1c 100644 --- a/README.rst +++ b/README.rst @@ -238,7 +238,7 @@ header-only library so it doesn't provide any linkage options. Quick build -~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~ You can download and install fmt using the `vcpkg `_ dependency manager:: @@ -248,7 +248,10 @@ You can download and install fmt using the `vcpkg `_ on the vcpkg repository. +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 `_ +on the vcpkg repository. Running the tests ~~~~~~~~~~~~~~~~~ From 06e437fd98575bc31bf812ef349c8e0531f644d7 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 18 Jan 2020 06:59:21 -0800 Subject: [PATCH 15/31] Move docs to the proper place --- README.rst | 17 ----------------- doc/usage.rst | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 19ea3a1c..34427826 100644 --- a/README.rst +++ b/README.rst @@ -236,23 +236,6 @@ Folly Format 79.9 445 430 compare formatting function overhead only. Boost Format is a header-only library so it doesn't provide any linkage options. - -Quick build -~~~~~~~~~~~ - -You can download and install fmt using the `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 `_ -on the vcpkg repository. - Running the tests ~~~~~~~~~~~~~~~~~ diff --git a/doc/usage.rst b/doc/usage.rst index dacb4f03..d42719e0 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -114,6 +114,22 @@ fmt can be installed on Linux, macOS and Windows with conda install -c conda-forge fmt +Vcpkg +===== + +You can download and install fmt using the `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 `__ on the vcpkg repository. + Android NDK =========== From bd5f903f28579d9547a4b1763c1a39d1fba5c241 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 18 Jan 2020 07:11:45 -0800 Subject: [PATCH 16/31] Add a locale example --- doc/api.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index 0834a874..4e370252 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -85,6 +85,19 @@ Compatibility .. doxygentypedef:: fmt::string_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 + #include + + std::locale::global(std::locale("en_US.UTF-8")); + auto s = fmt::format("{:n}", 1000000); // s == "1,000,000" + .. _format-api: Format API From 9bd9738da024a12f79a72e664aaeaf2004d2e640 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 18 Jan 2020 07:43:59 -0800 Subject: [PATCH 17/31] Remove static and simplify names --- include/fmt/chrono.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index ca4ed30a..a77d86ad 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -760,14 +760,14 @@ inline std::chrono::duration get_milliseconds( } template -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); return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", val); } template -static OutputIt format_chrono_duration_unit(OutputIt out) { +OutputIt format_duration_unit(OutputIt out) { if (const char* unit = get_units()) return format_to(out, "{}", unit); if (Period::den == 1) return format_to(out, "[{}]s", Period::num); return format_to(out, "[{}/{}]s", Period::num, Period::den); @@ -986,10 +986,10 @@ struct chrono_formatter { void on_duration_value() { if (handle_nan_inf()) return; write_sign(); - out = format_chrono_duration_value(out, val, precision); + out = format_duration_value(out, val, precision); } - void on_duration_unit() { out = format_chrono_duration_unit(out); } + void on_duration_unit() { out = format_duration_unit(out); } }; } // namespace internal @@ -1088,8 +1088,8 @@ struct formatter, Char> { internal::handle_dynamic_spec( precision, precision_ref, ctx); if (begin == end || *begin == '}') { - out = internal::format_chrono_duration_value(out, d.count(), precision); - internal::format_chrono_duration_unit(out); + out = internal::format_duration_value(out, d.count(), precision); + internal::format_duration_unit(out); } else { internal::chrono_formatter f( ctx, out, d); From 75765bfad576904b8910755921af814cfdbc1107 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 18 Jan 2020 08:19:27 -0800 Subject: [PATCH 18/31] Avoid unnecessary unsigned overflows (#1515) --- include/fmt/format-inl.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index d812c3e1..dcad80ad 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -386,7 +386,8 @@ class fp { const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; auto u = bit_cast(d); f = u & significand_mask; - auto biased_e = (u & exponent_mask) >> double_significand_size; + int biased_e = + static_cast((u & exponent_mask) >> double_significand_size); // Predecessor is closer if d is a normalized power of 2 (f == 0) other than // the smallest normalized number (biased_e > 1). bool is_predecessor_closer = f == 0 && biased_e > 1; @@ -394,7 +395,7 @@ class fp { f += implicit_bit; else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = static_cast(biased_e - exponent_bias - double_significand_size); + e = biased_e - exponent_bias - double_significand_size; 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 // (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. 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( - static_cast( - (min_exponent + fp::significand_size - 1) * one_over_log2_10 + - ((uint64_t(1) << 32) - 1) // ceil - ) >> - 32 // arithmetic shift + ((min_exponent + fp::significand_size - 1) * one_over_log2_10 + + ((int64_t(1) << 32) - 1)) // ceil + >> 32 // arithmetic shift ); // Decimal exponent of the first (smallest) cached power of 10. const int first_dec_exp = -348; From e5f2f8ce7aad846caf1097289d21176b93a08526 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Jan 2020 13:22:49 -0800 Subject: [PATCH 19/31] Add variable-width fill support (#1109) --- include/fmt/chrono.h | 2 +- include/fmt/format.h | 75 ++++++++++++++++++++++++++++++++++---------- test/format-test.cc | 3 +- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index a77d86ad..dd003202 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1024,7 +1024,7 @@ struct formatter, Char> { } 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 fill) { f.specs.fill = fill; } void on_align(align_t align) { f.specs.align = align; } void on_width(int width) { f.specs.width = width; } void on_precision(int _precision) { f.precision = _precision; } diff --git a/include/fmt/format.h b/include/fmt/format.h index b6a26f3b..06ecf557 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -962,9 +962,28 @@ template struct null {}; // Workaround an array initialization issue in gcc 4.8. template struct fill_t { private: - Char data_[6]; + enum { max_size = 5 }; + Char data_[max_size + 1]; public: + FMT_CONSTEXPR void operator=(basic_string_view 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]; + data_[size] = Char(); + } + + size_t size() const { + size_t i = 1; + while (data_[i] && i <= max_size) ++i; + return i; + } + + const Char* data() const { return data_; } + FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } FMT_CONSTEXPR const Char& operator[](size_t index) const { return data_[index]; @@ -1352,6 +1371,14 @@ template struct nonfinite_writer { } }; +template +OutputIt fill(OutputIt it, size_t n, const fill_t& 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 // character range. template class basic_writer { @@ -1614,20 +1641,20 @@ template class basic_writer { size_t size = f.size(); // The number of code units. size_t num_code_points = width != 0 ? f.width() : size; if (width <= num_code_points) return f(reserve(size)); - auto&& it = reserve(width + (size - num_code_points)); - char_type fill = specs.fill[0]; - std::size_t padding = width - num_code_points; + size_t padding = width - num_code_points; + size_t fill_size = specs.fill.size(); + auto&& it = reserve(size + padding * fill_size); if (specs.align == align::right) { - it = std::fill_n(it, padding, fill); + it = fill(it, padding, specs.fill); f(it); } else if (specs.align == align::center) { std::size_t left_padding = padding / 2; - it = std::fill_n(it, left_padding, fill); + it = fill(it, left_padding, specs.fill); f(it); - it = std::fill_n(it, padding - left_padding, fill); + it = fill(it, padding - left_padding, specs.fill); } else { f(it); - it = std::fill_n(it, padding, fill); + it = fill(it, padding, specs.fill); } } @@ -2008,7 +2035,9 @@ template class specs_setter { : specs_(other.specs_) {} 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 fill) { + specs_.fill = fill; + } FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } @@ -2320,16 +2349,25 @@ template struct precision_adapter { SpecHandler& handler; }; +template +FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) { + if (sizeof(Char) != 1 || (*begin & 0x80) == 0) return begin + 1; + do { + ++begin; + } while (begin != end && (*begin & 0xc0) == 0x80); + return begin; +} + // Parses fill and alignment. template FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, Handler&& handler) { FMT_ASSERT(begin != end, ""); auto align = align::none; - int i = 0; - if (begin + 1 != end) ++i; - do { - switch (static_cast(begin[i])) { + auto p = next_code_point(begin, end); + if (p == end) p = begin; + for (;;) { + switch (static_cast(*p)) { case '<': align = align::left; break; @@ -2346,18 +2384,21 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, break; } if (align != align::none) { - if (i > 0) { + if (p != begin) { auto c = *begin; if (c == '{') return handler.on_error("invalid fill character '{'"), begin; - begin += 2; - handler.on_fill(c); + handler.on_fill(basic_string_view(begin, p - begin)); + begin = p + 1; } else ++begin; handler.on_align(align); break; + } else if (p == begin) { + break; } - } while (i-- > 0); + p = begin; + } return begin; } diff --git a/test/format-test.cc b/test/format-test.cc index 76f57106..015a7438 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -815,6 +815,7 @@ TEST(FormatterTest, Fill) { EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast(0xface))); EXPECT_EQ("foo=", format("{:}=", "foo")); EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*')); + EXPECT_EQ("жж42", format("{0:ж>4}", 42)); } TEST(FormatterTest, PlusSign) { @@ -2173,7 +2174,7 @@ struct test_format_specs_handler { type(other.type) {} 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_minus() { res = MINUS; } FMT_CONSTEXPR void on_space() { res = SPACE; } From 8a3a8177d66b3e887bc27f30215cccd890ccfed2 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Jan 2020 15:34:30 -0800 Subject: [PATCH 20/31] Bump version --- include/fmt/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 00e2c3a3..5aefcc14 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -15,7 +15,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 60102 +#define FMT_VERSION 60103 #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) From b4218aa0f83edd3867c8ec6937204e45039bfb21 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Jan 2020 15:57:11 -0800 Subject: [PATCH 21/31] Test invalid fill --- include/fmt/format.h | 15 ++++++--------- test/format-test.cc | 2 ++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 06ecf557..76e4fac6 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -962,8 +962,9 @@ template struct null {}; // Workaround an array initialization issue in gcc 4.8. template struct fill_t { private: - enum { max_size = 5 }; - Char data_[max_size + 1]; + enum { max_size = 4 }; + Char data_[max_size]; + unsigned char size_; public: FMT_CONSTEXPR void operator=(basic_string_view s) { @@ -973,15 +974,10 @@ template struct fill_t { return; } for (size_t i = 0; i < size; ++i) data_[i] = s[i]; - data_[size] = Char(); - } - - size_t size() const { - size_t i = 1; - while (data_[i] && i <= max_size) ++i; - return i; + size_ = static_cast(size); } + size_t size() const { return size_; } const Char* data() const { return data_; } FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } @@ -992,6 +988,7 @@ template struct fill_t { static FMT_CONSTEXPR fill_t make() { auto fill = fill_t(); fill[0] = Char(' '); + fill.size_ = 1; return fill; } }; diff --git a/test/format-test.cc b/test/format-test.cc index 015a7438..8bce69be 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -816,6 +816,8 @@ TEST(FormatterTest, Fill) { EXPECT_EQ("foo=", format("{:}=", "foo")); 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) { From 7800173eb17986119f8a6e0c47ca46f0022161c0 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Jan 2020 18:57:32 -0800 Subject: [PATCH 22/31] Update fill docs --- doc/syntax.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index 56b70f07..4d50fc4e 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -76,7 +76,7 @@ The general form of a *standard format specifier* is: .. productionlist:: sf format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`][`type`] - fill: + fill: align: "<" | ">" | "^" sign: "+" | "-" | " " 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" int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X" -The *fill* character can be any character other than '{', '}' or '\\0'. The -presence of a fill character is signaled by the character following it, which -must be one of the alignment options. If the second character of *format_spec* -is not a valid alignment option, then it is assumed that both the fill character -and the alignment option are absent. +The *fill* character can be any Unicode code point other than ``'{'`` or +``'}'``. The presence of a fill character is signaled by the character following +it, which must be one of the alignment options. If the second character of +*format_spec* is not a valid alignment option, then it is assumed that both the +fill character and the alignment option are absent. The meaning of the various alignment options is as follows: From 47d3968092d3ceb210d9d319db9694313ea2ef9b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Jan 2020 19:15:54 -0800 Subject: [PATCH 23/31] Add more examples --- doc/syntax.rst | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index 4d50fc4e..c95ec93d 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -372,6 +372,33 @@ Padded hex byte with prefix and always prints both hex characters:: format("{:#04x}", 0); // Result: "0x00" +Box drawing using Unicode fill:: + + fmt::print( + "┌{0:─^{2}}┐\n" + "│{1: ^{2}}│\n" + "└{0:─^{2}}┘\n", "", "Hello, world!", 20); + +prints:: + + ┌────────────────────┐ + │ Hello, world! │ + └────────────────────┘ + +Using type-specific formatting:: + + #include + + auto t = tm(); + 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 + .. ifconfig:: False Using the comma as a thousands separator:: @@ -379,13 +406,6 @@ Padded hex byte with prefix and always prints both hex characters:: format("{:,}", 1234567890); '1,234,567,890' - Using type-specific formatting:: - - >>> import datetime - >>> d = datetime.datetime(2010, 7, 4, 12, 15, 58) - Format("{:%Y-%m-%d %H:%M:%S}") << d) - '2010-07-04 12:15:58' - Nesting arguments and more complex examples:: >>> for align, text in zip('<^>', ['left', 'center', 'right']): From a844d7ab81b67e95f85a04eb68d4ca66f7077fae Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 19 Jan 2020 19:20:48 -0800 Subject: [PATCH 24/31] Add namespaces --- doc/syntax.rst | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index c95ec93d..f6a5ff3c 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -320,56 +320,56 @@ following examples. Accessing arguments by position:: - format("{0}, {1}, {2}", 'a', 'b', 'c'); + fmt::format("{0}, {1}, {2}", 'a', 'b', 'c'); // Result: "a, b, c" - format("{}, {}, {}", 'a', 'b', 'c'); + fmt::format("{}, {}, {}", '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" - 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" Aligning the text and specifying a width:: - format("{:<30}", "left aligned"); + fmt::format("{:<30}", "left aligned"); // Result: "left aligned " - format("{:>30}", "right aligned"); + fmt::format("{:>30}", "right aligned"); // Result: " right aligned" - format("{:^30}", "centered"); + fmt::format("{:^30}", "centered"); // Result: " centered " - format("{:*^30}", "centered"); // use '*' as a fill char + fmt::format("{:*^30}", "centered"); // use '*' as a fill char // Result: "***********centered***********" Dynamic width:: - format("{:<{}}", "left aligned", 30); + fmt::format("{:<{}}", "left aligned", 30); // Result: "left aligned " Dynamic precision:: - format("{:.{}f}", 3.14, 1); + fmt::format("{:.{}f}", 3.14, 1); // Result: "3.1" 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" - 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" - 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" 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" // 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" Padded hex byte with prefix and always prints both hex characters:: - format("{:#04x}", 0); + fmt::format("{:#04x}", 0); // Result: "0x00" Box drawing using Unicode fill:: From fd1cabe464d7bed9739e7b52ef81eea936646098 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 20 Jan 2020 06:37:14 -0800 Subject: [PATCH 25/31] Workaround a bogus MSVC warning --- include/fmt/format.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 76e4fac6..cc9e5b4e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -221,7 +221,7 @@ namespace internal { // A helper function to suppress bogus "conditional expression is constant" // warnings. -template inline T const_check(T value) { return value; } +template FMT_CONSTEXPR T const_check(T value) { return value; } // An equivalent of `*reinterpret_cast(&source)` that doesn't have // undefined behavior (e.g. due to type aliasing). @@ -2348,7 +2348,7 @@ template struct precision_adapter { template FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) { - if (sizeof(Char) != 1 || (*begin & 0x80) == 0) return begin + 1; + if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1; do { ++begin; } while (begin != end && (*begin & 0xc0) == 0x80); From 0b2eb6501c94ca6f95806569d546f93aba862abd Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 20 Jan 2020 08:42:16 -0800 Subject: [PATCH 26/31] Add locale example --- doc/syntax.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index f6a5ff3c..af65616a 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -399,13 +399,15 @@ Using type-specific formatting:: fmt::print("{:%Y-%m-%d %H:%M:%S}", t); // Prints: 2010-08-04 12:15:58 +Using the comma as a thousands separator:: + + #include + + auto s = fmt::format(std::locale("en_US.UTF-8"), "{:n}", 1234567890); + // s == "1,234,567,890" + .. ifconfig:: False - Using the comma as a thousands separator:: - - format("{:,}", 1234567890); - '1,234,567,890' - Nesting arguments and more complex examples:: >>> for align, text in zip('<^>', ['left', 'center', 'right']): From 25d6916b3ae5602a5cb1cf1bb8be1de3d2ecbd0f Mon Sep 17 00:00:00 2001 From: Greg Sjaardema Date: Tue, 21 Jan 2020 09:02:19 -0700 Subject: [PATCH 27/31] Fix so can work without locale defined If `FMT_STATIC_THOUSANDS_SEPARATOR` defined, then locale is not included or defined, so this call will be unresolved. I think this is the correct fix based on the code in `format-inl.h` and `format.h` --- include/fmt/ostream.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 75a8cd4b..c4831533 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -93,7 +93,9 @@ void format_value(buffer& buf, const T& value, locale_ref loc = locale_ref()) { formatbuf format_buf(buf); std::basic_ostream output(&format_buf); + #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) if (loc) output.imbue(loc.get()); + #endif output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value; buf.resize(buf.size()); From 9fc4161f5e905aef3cf8932334f4d5cf440c407a Mon Sep 17 00:00:00 2001 From: dspc-douglas Date: Wed, 22 Jan 2020 19:32:37 -0300 Subject: [PATCH 28/31] fix interal compiler error when building with mingw --- include/fmt/format-inl.h | 3 ++- src/format.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index dcad80ad..55bf1c44 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1147,7 +1147,8 @@ int snprintf_float(T value, int precision, float_specs specs, "fuzz mode - avoid large allocation inside snprintf"); #endif // 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 ? snprintf_ptr(begin, capacity, format, precision, value) : snprintf_ptr(begin, capacity, format, value); diff --git a/src/format.cc b/src/format.cc index 44ba77f0..e6fde7c3 100644 --- a/src/format.cc +++ b/src/format.cc @@ -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"); #endif // 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) : snprintf_ptr(buf, size, format, precision, value); } From 419db8baa1a8102eb333c5a7aa269efd198e1977 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 22 Jan 2020 18:03:34 -0800 Subject: [PATCH 29/31] Fix length computation of constexpr C strings --- include/fmt/format.h | 9 ++++++++- test/format-test.cc | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index cc9e5b4e..cd3bb03f 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3449,6 +3449,13 @@ template struct udl_arg { } }; +// A constexpr version of strlen. +template FMT_CONSTEXPR size_t compute_length(const Char* s) { + size_t len = 0; + while (*s++) ++len; + return len; +} + } // namespace internal inline namespace literals { @@ -3512,7 +3519,7 @@ FMT_END_NAMESPACE using char_type = fmt::remove_cvref_t; \ __VA_ARGS__ FMT_CONSTEXPR \ operator fmt::basic_string_view() const { \ - return {s, sizeof(s) / sizeof(char_type) - 1}; \ + return {s, fmt::internal::compute_length(s)}; \ } \ }; \ return FMT_STRING(); \ diff --git a/test/format-test.cc b/test/format-test.cc index 8bce69be..f479ceb6 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1849,7 +1849,10 @@ TEST(FormatTest, UnpackedArgs) { struct string_like {}; fmt::string_view to_string_view(string_like) { return "foo"; } +FMT_CONSTEXPR_DECL const char* format_str_ptr = "0123456789"; + TEST(FormatTest, CompileTimeString) { + EXPECT_EQ(format_str_ptr, fmt::format(FMT_STRING(format_str_ptr))); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); From 09a13244c85de35471ff7a660b17360111b14430 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 22 Jan 2020 19:08:06 -0800 Subject: [PATCH 30/31] Disallow passing non-string-literals to FMT_STRING --- include/fmt/format.h | 14 ++++++-------- test/format-test.cc | 3 --- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index cd3bb03f..59dc223b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3449,13 +3449,10 @@ template struct udl_arg { } }; -// A constexpr version of strlen. -template FMT_CONSTEXPR size_t compute_length(const Char* s) { - size_t len = 0; - while (*s++) ++len; - return len; +template +FMT_CONSTEXPR basic_string_view literal_to_view(const Char (&s)[N]) { + return {s, N - 1}; } - } // namespace internal inline namespace literals { @@ -3519,7 +3516,8 @@ FMT_END_NAMESPACE using char_type = fmt::remove_cvref_t; \ __VA_ARGS__ FMT_CONSTEXPR \ operator fmt::basic_string_view() const { \ - return {s, fmt::internal::compute_length(s)}; \ + /* FMT_STRING only accepts string literals. */ \ + return fmt::internal::literal_to_view(s); \ } \ }; \ return FMT_STRING(); \ @@ -3527,7 +3525,7 @@ FMT_END_NAMESPACE /** \rst - Constructs a compile-time format string. + Constructs a compile-time format string from a string literal *s*. **Example**:: diff --git a/test/format-test.cc b/test/format-test.cc index f479ceb6..8bce69be 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1849,10 +1849,7 @@ TEST(FormatTest, UnpackedArgs) { struct string_like {}; fmt::string_view to_string_view(string_like) { return "foo"; } -FMT_CONSTEXPR_DECL const char* format_str_ptr = "0123456789"; - TEST(FormatTest, CompileTimeString) { - EXPECT_EQ(format_str_ptr, fmt::format(FMT_STRING(format_str_ptr))); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); From 1acb73f970beaa16a67ad0515362692f1716d64a Mon Sep 17 00:00:00 2001 From: zeffy Date: Thu, 23 Jan 2020 18:48:36 -0800 Subject: [PATCH 31/31] Fix formatting std::chrono::duration types to wide strings (#1533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix formatting chrono durations to wide strings * Make format buffers const correct * Add FormatWide chrono test case * Fix incorrect wide encoding of 'µs' I think might be a source file encoding issue, so I used \u00B5 instead. * Update FormatWide test to use proper encoding of µs * Revert changes to format_localized's parameters * Use different overload of `std::time_put::put` to avoid needing a format string * Use utf8_to_utf16 instead of having redundant overloads of get_units * Revert some minor changes * Remove FMT_CONSTEXPR from expression This should hopefully fix compilation on VS <2019 * Make suggested changes from code review * Run clang-format on chrono.h * Make sure unit isn't null before constructing a string_view from it --- include/fmt/chrono.h | 63 ++++++++++++++++++++++++++------------------ test/chrono-test.cc | 42 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 25 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index dd003202..80cbe697 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -8,14 +8,14 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ -#include "format.h" -#include "locale.h" - #include #include #include #include +#include "format.h" +#include "locale.h" + FMT_BEGIN_NAMESPACE // 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); break; case 'n': { - const char newline[] = "\n"; + const Char newline[]{'\n', 0}; handler.on_text(newline, newline + 1); break; } case 't': { - const char tab[] = "\t"; + const Char tab[]{'\t', 0}; handler.on_text(tab, tab + 1); break; } @@ -759,18 +759,30 @@ inline std::chrono::duration get_milliseconds( return std::chrono::duration(static_cast(ms)); } -template +template OutputIt format_duration_value(OutputIt out, Rep val, int precision) { - if (precision >= 0) return format_to(out, "{:.{}f}", val, precision); - return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", + const Char pr_f[]{'{', ':', '.', '{', '}', 'f', '}', 0}; + 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::value ? fp_f : format, val); } -template +template OutputIt format_duration_unit(OutputIt out) { - if (const char* unit = get_units()) return format_to(out, "{}", unit); - if (Period::den == 1) return format_to(out, "[{}]s", Period::num); - return format_to(out, "[{}/{}]s", Period::num, Period::den); + if (const char* unit = get_units()) { + string_view s(unit); + if (const_check(std::is_same())) { + 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 (); auto& facet = std::use_facet>(locale); std::basic_ostringstream os; os.imbue(locale); - facet.put(os, os, ' ', &time, format, format + std::strlen(format)); + facet.put(os, os, ' ', &time, format, modifier); auto str = os.str(); std::copy(str.begin(), str.end(), out); } @@ -907,7 +919,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(hour(), 2); auto time = tm(); time.tm_hour = to_nonnegative_int(hour(), 24); - format_localized(time, "%OH"); + format_localized(time, 'H', 'O'); } void on_12_hour(numeric_system ns) { @@ -916,7 +928,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(hour12(), 2); auto time = tm(); time.tm_hour = to_nonnegative_int(hour12(), 12); - format_localized(time, "%OI"); + format_localized(time, 'I', 'O'); } void on_minute(numeric_system ns) { @@ -925,7 +937,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(minute(), 2); auto time = tm(); time.tm_min = to_nonnegative_int(minute(), 60); - format_localized(time, "%OM"); + format_localized(time, 'M', 'O'); } void on_second(numeric_system ns) { @@ -950,13 +962,12 @@ struct chrono_formatter { } auto time = tm(); time.tm_sec = to_nonnegative_int(second(), 60); - format_localized(time, "%OS"); + format_localized(time, 'S', 'O'); } void on_12_hour_time() { if (handle_nan_inf()) return; - - format_localized(time(), "%r"); + format_localized(time(), 'r'); } void on_24_hour_time() { @@ -980,16 +991,18 @@ struct chrono_formatter { void on_am_pm() { if (handle_nan_inf()) return; - format_localized(time(), "%p"); + format_localized(time(), 'p'); } void on_duration_value() { if (handle_nan_inf()) return; write_sign(); - out = format_duration_value(out, val, precision); + out = format_duration_value(out, val, precision); } - void on_duration_unit() { out = format_duration_unit(out); } + void on_duration_unit() { + out = format_duration_unit(out); + } }; } // namespace internal @@ -1088,8 +1101,8 @@ struct formatter, Char> { internal::handle_dynamic_spec( precision, precision_ref, ctx); if (begin == end || *begin == '}') { - out = internal::format_duration_value(out, d.count(), precision); - internal::format_duration_unit(out); + out = internal::format_duration_value(out, d.count(), precision); + internal::format_duration_unit(out); } else { internal::chrono_formatter f( ctx, out, d); diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 04b5f2fa..828a8aee 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -145,6 +145,48 @@ TEST(ChronoTest, FormatDefault) { fmt::format("{}", std::chrono::duration>(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(42))); + EXPECT_EQ(L"42fs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42ps", + fmt::format(L"{}", std::chrono::duration(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(42))); + EXPECT_EQ(L"42ds", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); + EXPECT_EQ(L"42das", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42hs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42ks", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Ms", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Gs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Ts", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Ps", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Es", + fmt::format(L"{}", std::chrono::duration(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>(42))); + EXPECT_EQ( + L"42[15/4]s", + fmt::format(L"{}", std::chrono::duration>(42))); +} + TEST(ChronoTest, Align) { auto s = std::chrono::seconds(42); EXPECT_EQ("42s ", fmt::format("{:5}", s));