From 3cf12d7b11e6eaf80b58ac06d30ea5cf90030fc9 Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Wed, 29 May 2019 07:35:17 +0200 Subject: [PATCH 1/5] add gcc 8 c++17 build --- .travis.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.travis.yml b/.travis.yml index b437efd9a55b..98738081e8b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,17 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-6 + # g++ 8 on Linux with C++17 + - env: COMPILER=g++-8 BUILD=Debug STANDARD=17 + compiler: gcc + addons: + apt: + update: true + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + # Apple clang on OS X with C++14 - env: BUILD=Debug STANDARD=14 compiler: clang From ad360a62b011f0542a33d219c69262f71a3b98f9 Mon Sep 17 00:00:00 2001 From: Paul Dreik Date: Wed, 29 May 2019 08:02:44 +0200 Subject: [PATCH 2/5] add gcc 8 c++17 release build --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 98738081e8b2..9de3815b447a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,15 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-8 + - env: COMPILER=g++-8 BUILD=Release STANDARD=17 + compiler: gcc + addons: + apt: + update: true + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 # Apple clang on OS X with C++14 - env: BUILD=Debug STANDARD=14 From afc571aedc3f04f3cc209b050466768ffb0cb7a2 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 29 May 2019 10:27:05 -0700 Subject: [PATCH 3/5] Document join and relax its compiler requirements --- doc/api.rst | 8 ++++---- include/fmt/format.h | 40 +++++++++++++++++++++++++++------------- test/format-test.cc | 2 +- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index ed2a1e4e5754..8a226758bc88 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -180,10 +180,6 @@ You can also write a formatter for a hierarchy of classes:: fmt::print("{}", a); // prints "B" } -This section shows how to define a custom format function for a user-defined -type. The next section describes how to get ``fmt`` to use a conventional stream -output ``operator<<`` when one is defined for a user-defined type. - Output Iterator Support ----------------------- @@ -214,6 +210,10 @@ Utilities .. doxygenfunction:: fmt::to_string_view(basic_string_view) +.. doxygenfunction:: fmt::join(const Range&, string_view) + +.. doxygenfunction:: fmt::join(It, It, string_view) + .. doxygenclass:: fmt::basic_memory_buffer :protected-members: :members: diff --git a/include/fmt/format.h b/include/fmt/format.h index 32317c77a595..ca56fab0d3c2 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -162,13 +162,6 @@ FMT_END_NAMESPACE # endif #endif -#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || \ - FMT_MSC_VER >= 1600 -# define FMT_USE_TRAILING_RETURN 1 -#else -# define FMT_USE_TRAILING_RETURN 0 -#endif - #ifdef FMT_USE_INT128 // Do nothing. #elif defined(__SIZEOF_INT128__) @@ -291,6 +284,12 @@ FMT_CONSTEXPR T* end(T (&array)[N]) FMT_NOEXCEPT { return array + N; } +// An implementation of iterator_t for pre-C++20 compilers such as gcc 4. +template +struct iterator_t { + typedef decltype(internal::begin(internal::declval())) type; +}; + // For std::result_of in gcc 4.4. template struct function { template struct result { typedef Result type; }; @@ -3302,6 +3301,10 @@ struct formatter, Char> } }; +/** + Returns an object that formats the iterator range `[begin, end)` with elements + separated by `sep`. + */ template arg_join join(It begin, It end, string_view sep) { return arg_join(begin, end, sep); @@ -3312,17 +3315,28 @@ arg_join join(It begin, It end, wstring_view sep) { return arg_join(begin, end, sep); } -// The following causes ICE in gcc 4.4. -#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405) +// gcc 4.4 on join: internal compiler error: in tsubst_copy, at cp/pt.c:10122 +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 405 +/** + \rst + Returns an object that formats `range` with elements separated by `sep`. + + **Example**:: + + std::vector v = {1, 2, 3}; + fmt::print("{}", fmt::join(v, ", ")); + // Output: "1, 2, 3" + \endrst + */ template -auto join(const Range& range, string_view sep) - -> arg_join { +arg_join::type, char> + join(const Range& range, string_view sep) { return join(internal::begin(range), internal::end(range), sep); } template -auto join(const Range& range, wstring_view sep) - -> arg_join { +arg_join::type, wchar_t> + join(const Range& range, wstring_view sep) { return join(internal::begin(range), internal::end(range), sep); } #endif diff --git a/test/format-test.cc b/test/format-test.cc index 14a7f87a0483..0b4b2176d409 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1793,7 +1793,7 @@ TEST(FormatTest, JoinArg) { EXPECT_EQ(format("{}, {}", v3[0], v3[1]), format("{}", join(v3, v3 + 2, ", "))); -#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405) +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 405 EXPECT_EQ("(1, 2, 3)", format("({})", join(v1, ", "))); EXPECT_EQ("(+01.20, +03.40)", format("({:+06.2f})", join(v2, ", "))); #endif From e5512c5d5762099b35c59a299e83add6b4c09e43 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 29 May 2019 13:21:24 -0700 Subject: [PATCH 4/5] Use static_assert instead of SFINAE in arg(...) --- include/fmt/core.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 0d1b55236383..4283927725e5 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1416,8 +1416,9 @@ typename buffer_context::type::iterator vformat_to( fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); \endrst */ -template ::value)> +template inline internal::named_arg arg(const S& name, const T& arg) { + static_assert(internal::is_string::value, ""); return {name, arg}; } @@ -1472,6 +1473,7 @@ inline std::basic_string vformat( std::string message = fmt::format("The answer is {}", 42); \endrst */ +// Use SFINAE instead of static_assert because of color overload of fmt::format. template ::value)> inline std::basic_string format(const S& format_str, From 30bce6c14cfaefaf799cbb9d3110ee7f12090345 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 29 May 2019 18:02:26 -0700 Subject: [PATCH 5/5] Fix a few chrono formatting corner cases (#1178) --- include/fmt/chrono.h | 34 ++++++++++++++++++++-------------- test/chrono-test.cc | 18 ++++++++++++++++-- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 06ced17360c2..926600e22bb3 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -419,13 +419,10 @@ inline std::chrono::duration get_milliseconds( } template -OutputIt static format_chrono_duration_value(OutputIt out, Rep val, - int precision) { - if (precision < 0) { - return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", - val); - } - return format_to(out, "{:.{}f}", val, precision); +OutputIt format_chrono_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 @@ -441,20 +438,20 @@ struct chrono_formatter { FormatContext& context; OutputIt out; int precision; - Rep val; - typedef std::chrono::duration seconds; + typedef typename std::conditional::value && + sizeof(Rep) < sizeof(int), + int, Rep>::type rep; + rep val; + typedef std::chrono::duration seconds; seconds s; - typedef std::chrono::duration milliseconds; + typedef std::chrono::duration milliseconds; typedef typename FormatContext::char_type char_type; explicit chrono_formatter(FormatContext& ctx, OutputIt o, std::chrono::duration d) : context(ctx), out(o), val(d.count()) { - if (d.count() < 0) { - d = -d; - *out++ = '-'; - } + if (d.count() < 0) d = -d; s = std::chrono::duration_cast(d); } @@ -476,7 +473,15 @@ struct chrono_formatter { return time; } + void write_sign() { + if (val < 0) { + *out++ = '-'; + val = -val; + } + } + void write(Rep value, int width) { + write_sign(); if (isnan(value)) return write_nan(); typedef typename int_traits::main_type main_type; main_type n = to_unsigned(to_int(value)); @@ -570,6 +575,7 @@ struct chrono_formatter { void on_am_pm() { format_localized(time(), "%p"); } void on_duration_value() { + write_sign(); out = format_chrono_duration_value(out, val, precision); } diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 2179e0e7b3b7..daeca5f4e0f1 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -306,12 +306,22 @@ TEST(ChronoTest, InvalidColons) { fmt::format_error); } +TEST(ChronoTest, NegativeDuration) { + EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-03:25:45", + fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00:01", + fmt::format("{:%M:%S}", std::chrono::duration(-1))); + EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00.127", + fmt::format("{:%S}", + std::chrono::duration{-127})); +} + TEST(ChronoTest, SpecialDurations) { EXPECT_EQ( "40.", fmt::format("{:%S}", std::chrono::duration(1e20)).substr(0, 3)); - EXPECT_EQ("-00:01", - fmt::format("{:%M:%S}", std::chrono::duration(-1))); auto nan = std::numeric_limits::quiet_NaN(); EXPECT_EQ( "nan nan nan nan.nan nan:nan nan", @@ -322,6 +332,10 @@ TEST(ChronoTest, SpecialDurations) { "1Es"); EXPECT_EQ(fmt::format("{}", std::chrono::duration(1)), "1as"); + EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration{2}), + "03:33"); + EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), + "03:33:20"); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR