Add C++11 support, use template recursion if FMT_USE_CONSTEXPR is not defined

This commit is contained in:
Filip Matracki 2021-10-19 22:44:42 +02:00
parent 7a6dc3abbe
commit c4b9c27936
2 changed files with 75 additions and 32 deletions

View File

@ -875,6 +875,57 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type; using type = typename std::make_unsigned<T>::type;
}; };
#if FMT_USE_CONSTEXPR
template <class Duration> 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 <std::uint64_t n, std::uint64_t d, unsigned w = 0,
bool should_continue = n % d != 0 && (w < 19)>
struct width {
static_assert(d > 0, "width called with zero denominator");
static constexpr unsigned value = 1 + width<n % d * 10, d, w + 1>::value;
};
template <std::uint64_t n, std::uint64_t d, unsigned w>
struct width<n, d, w, false> {
static constexpr unsigned value = 0;
};
template <unsigned exp> struct static_pow10 {
private:
static constexpr std::uint64_t h = static_pow10<exp / 2>::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 #if FMT_SAFE_DURATION_CAST
// throwing version of safe_duration_cast // throwing version of safe_duration_cast
template <typename To, typename FromRep, typename FromPeriod> template <typename To, typename FromRep, typename FromPeriod>
@ -887,30 +938,22 @@ To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
#endif #endif
template <class Duration> struct subsecond_helper { template <class Duration> struct subsecond_helper {
static constexpr unsigned int fractional_width = [] { static constexpr std::uintmax_t fractional_width =
static_assert(Duration::period::num > 0 && Duration::period::den > 0, #if FMT_USE_CONSTEXPR
"Numerator and denominator can't be less than 1."); get_fractional_width<Duration>();
auto num = Duration::period::num; #else
constexpr auto den = Duration::period::den; width<Duration::period::num, Duration::period::den>::value;
// Returns the number of fractional digits of num / den in the range #endif
// [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 intmax_t pow10(const unsigned int exp) { static constexpr std::uintmax_t fractional_width_pow10 =
intmax_t result = 1; #if FMT_USE_CONSTEXPR
for (unsigned int i = 0; i < exp; ++i) { pow10(fractional_width);
result *= 10; #else
} static_pow10<fractional_width>::value;
return result; #endif
}
template <class Rep, class Period> template <class Rep, class Period>
static constexpr std::chrono::duration<Rep, Period> abs( static FMT_CONSTEXPR std::chrono::duration<Rep, Period> abs(
std::chrono::duration<Rep, Period> d) { std::chrono::duration<Rep, Period> d) {
if FMT_CONSTEXPR (std::chrono::duration<Rep, Period>::min() < if FMT_CONSTEXPR (std::chrono::duration<Rep, Period>::min() <
std::chrono::duration<Rep, Period>::zero()) { std::chrono::duration<Rep, Period>::zero()) {
@ -923,13 +966,13 @@ template <class Duration> struct subsecond_helper {
using precision = std::chrono::duration< using precision = std::chrono::duration<
typename std::common_type<typename Duration::rep, typename std::common_type<typename Duration::rep,
std::chrono::seconds::rep>::type, std::chrono::seconds::rep>::type,
std::ratio<1, pow10(fractional_width)>>; std::ratio<1, fractional_width_pow10>>;
template <class Rep, class Period> template <class Rep, class Period>
static constexpr precision get_subseconds( static FMT_CONSTEXPR precision
std::chrono::duration<Rep, Period> d) { get_subseconds(std::chrono::duration<Rep, Period> d) {
if FMT_CONSTEXPR (std::chrono::treat_as_floating_point_v< if FMT_CONSTEXPR (std::chrono::treat_as_floating_point<
typename precision::rep>) { typename precision::rep>::value) {
return abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d); return abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
} else { } else {
return std::chrono::duration_cast<precision>( return std::chrono::duration_cast<precision>(

View File

@ -536,18 +536,18 @@ TEST(chrono_test, weekday) {
TEST(chrono_test, cpp20_duration_subsecond_support) { TEST(chrono_test, cpp20_duration_subsecond_support) {
using attoseconds = std::chrono::duration<std::intmax_t, std::atto>; using attoseconds = std::chrono::duration<std::intmax_t, std::atto>;
// Check that 18 digits of subsecond precision are supported // 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"); "00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673'231'113'420'148'734}), EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}),
"-00.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"); "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"); "-13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234"); EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234");
{ {
// Check that {:%H:%M:%S} is equivalent to {:%T} // 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); auto formatted_dur = fmt::format("{:%T}", dur);
EXPECT_EQ(formatted_dur, "01:00:01.234"); EXPECT_EQ(formatted_dur, "01:00:01.234");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur); 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 // Check that durations with precision greater than std::chrono::seconds
// with no subsecond part print as regular 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 #endif // FMT_STATIC_THOUSANDS_SEPARATOR