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;
};
#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
// throwing version of safe_duration_cast
template <typename To, typename FromRep, typename FromPeriod>
@ -887,30 +938,22 @@ To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
#endif
template <class Duration> 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<Duration>();
#else
width<Duration::period::num, Duration::period::den>::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<fractional_width>::value;
#endif
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) {
if FMT_CONSTEXPR (std::chrono::duration<Rep, Period>::min() <
std::chrono::duration<Rep, Period>::zero()) {
@ -923,13 +966,13 @@ template <class Duration> struct subsecond_helper {
using precision = std::chrono::duration<
typename std::common_type<typename Duration::rep,
std::chrono::seconds::rep>::type,
std::ratio<1, pow10(fractional_width)>>;
std::ratio<1, fractional_width_pow10>>;
template <class Rep, class Period>
static constexpr precision get_subseconds(
std::chrono::duration<Rep, Period> d) {
if FMT_CONSTEXPR (std::chrono::treat_as_floating_point_v<
typename precision::rep>) {
static FMT_CONSTEXPR precision
get_subseconds(std::chrono::duration<Rep, Period> d) {
if FMT_CONSTEXPR (std::chrono::treat_as_floating_point<
typename precision::rep>::value) {
return abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
} else {
return std::chrono::duration_cast<precision>(

View File

@ -536,18 +536,18 @@ TEST(chrono_test, weekday) {
TEST(chrono_test, cpp20_duration_subsecond_support) {
using attoseconds = std::chrono::duration<std::intmax_t, std::atto>;
// 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