From c4b9c2793684b1b736e9ee57ea5c53214f7ab5da Mon Sep 17 00:00:00 2001 From: Filip Matracki Date: Tue, 19 Oct 2021 22:44:42 +0200 Subject: [PATCH] Add C++11 support, use template recursion if FMT_USE_CONSTEXPR is not defined --- include/fmt/chrono.h | 95 ++++++++++++++++++++++++++++++++------------ test/chrono-test.cc | 12 +++--- 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index e38f6dcf..e1595ba7 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -875,6 +875,57 @@ template struct make_unsigned_or_unchanged { using type = typename std::make_unsigned::type; }; +#if FMT_USE_CONSTEXPR +template FMT_CONSTEXPR unsigned int get_fractional_width() { + static_assert(Duration::period::num > 0 && Duration::period::den > 0, + "Numerator and denominator can't be less than 1."); + auto num = Duration::period::num; + constexpr auto den = Duration::period::den; + // Returns the number of fractional digits of num / den in the range + // [0, 18]. If it can't be represented, 6 is returned. Example: + // fractional_width(1, 8) would return 3 for 0.125. + unsigned int result = 0; + for (; num % den != 0 && result < 19; num = num % den * 10, ++result) { + } + return result == 19 ? 6 : result; +} +static FMT_CONSTEXPR intmax_t pow10(const unsigned int exp) { + std::intmax_t result = 1; + for (unsigned int i = 0; i < exp; ++i) { + result *= 10; + } + return result; +} +#else +// Workaround for C++11 +// Thanks to Howard Hinnant's library +// https://github.com/HowardHinnant/date/blob/master/include/date/date.h +// Copyright Howard Hinnant 2019 +template +struct width { + static_assert(d > 0, "width called with zero denominator"); + static constexpr unsigned value = 1 + width::value; +}; + +template +struct width { + static constexpr unsigned value = 0; +}; + +template struct static_pow10 { + private: + static constexpr std::uint64_t h = static_pow10::value; + + public: + static constexpr std::uint64_t value = h * h * (exp % 2 ? 10 : 1); +}; + +template <> struct static_pow10<0> { + static constexpr std::uint64_t value = 1; +}; +#endif + #if FMT_SAFE_DURATION_CAST // throwing version of safe_duration_cast template @@ -887,30 +938,22 @@ To fmt_safe_duration_cast(std::chrono::duration from) { #endif template struct subsecond_helper { - static constexpr unsigned int fractional_width = [] { - static_assert(Duration::period::num > 0 && Duration::period::den > 0, - "Numerator and denominator can't be less than 1."); - auto num = Duration::period::num; - constexpr auto den = Duration::period::den; - // Returns the number of fractional digits of num / den in the range - // [0, 18]. If it can't be represented, 6 is returned. Example: - // fractional_width(1, 8) would return 3 for 0.125. - unsigned int result = 0; - for (; num % den != 0 && result < 19; num = num % den * 10, ++result) { - } - return result == 19 ? 6 : result; - }(); + static constexpr std::uintmax_t fractional_width = +#if FMT_USE_CONSTEXPR + get_fractional_width(); +#else + width::value; +#endif - static constexpr intmax_t pow10(const unsigned int exp) { - intmax_t result = 1; - for (unsigned int i = 0; i < exp; ++i) { - result *= 10; - } - return result; - } + static constexpr std::uintmax_t fractional_width_pow10 = +#if FMT_USE_CONSTEXPR + pow10(fractional_width); +#else + static_pow10::value; +#endif template - static constexpr std::chrono::duration abs( + static FMT_CONSTEXPR std::chrono::duration abs( std::chrono::duration d) { if FMT_CONSTEXPR (std::chrono::duration::min() < std::chrono::duration::zero()) { @@ -923,13 +966,13 @@ template struct subsecond_helper { using precision = std::chrono::duration< typename std::common_type::type, - std::ratio<1, pow10(fractional_width)>>; + std::ratio<1, fractional_width_pow10>>; template - static constexpr precision get_subseconds( - std::chrono::duration d) { - if FMT_CONSTEXPR (std::chrono::treat_as_floating_point_v< - typename precision::rep>) { + static FMT_CONSTEXPR precision + get_subseconds(std::chrono::duration d) { + if FMT_CONSTEXPR (std::chrono::treat_as_floating_point< + typename precision::rep>::value) { return abs(d) - std::chrono::duration_cast(d); } else { return std::chrono::duration_cast( diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 4e19a1d5..ef9dbf59 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -536,18 +536,18 @@ TEST(chrono_test, weekday) { TEST(chrono_test, cpp20_duration_subsecond_support) { using attoseconds = std::chrono::duration; // Check that 18 digits of subsecond precision are supported - EXPECT_EQ(fmt::format("{:%S}", attoseconds{673'231'113'420'148'734}), + EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}), "00.673231113420148734"); - EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673'231'113'420'148'734}), + EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}), "-00.673231113420148734"); - EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13'420'148'734}), + EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13420148734}), "13.420148734"); - EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13'420'148'734}), + EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}), "-13.420148734"); EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234"); { // Check that {:%H:%M:%S} is equivalent to {:%T} - auto dur = std::chrono::milliseconds{3'601'234}; + auto dur = std::chrono::milliseconds{3601234}; auto formatted_dur = fmt::format("{:%T}", dur); EXPECT_EQ(formatted_dur, "01:00:01.234"); EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); @@ -568,6 +568,6 @@ TEST(chrono_test, cpp20_duration_subsecond_support) { } // Check that durations with precision greater than std::chrono::seconds // with no subsecond part print as regular seconds - EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7'000'000}), "07"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), "07"); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR