From: Tomasz Kamiński Date: Wed, 4 Jun 2025 09:05:11 +0000 (+0200) Subject: libstdc++: Fix format call and test formatting with empty specs for durations. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac0a04b7a254fb8e1d8d7088336bcb4375807b1e;p=thirdparty%2Fgcc.git libstdc++: Fix format call and test formatting with empty specs for durations. This patches fixes an obvious error, where the output iterator argument was missing for call to format_to, when duration with custom representation types are used. It's also adding the test for behavior of ostream operator and the formatting with empty chron-spec for the chrono types. Current coverage is: * duration and hh_mm_ss in this commit, * calendar types in r16-1016-g28a17985dd34b7. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__formatter_chrono:_M_s): Add missing __out argument to format_to call. * testsuite/std/time/format/empty_spec.cc: New test. Reviewed-by: Jonathan Wakely Signed-off-by: Tomasz Kamiński --- diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 346eb8b3c33..239f9c78009 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -1296,7 +1296,8 @@ namespace __format else { auto __str = std::format(_S_empty_spec, __ss.count()); - __out = std::format_to(_GLIBCXX_WIDEN("{:0>{}s}"), + __out = std::format_to(std::move(__out), + _GLIBCXX_WIDEN("{:0>{}s}"), __str, __hms.fractional_width); } diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc index 322faa1939d..46942dc30fc 100644 --- a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc +++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc @@ -1,7 +1,9 @@ // { dg-do run { target c++20 } } +// { dg-require-effective-target hosted } // { dg-timeout-factor 2 } #include +#include #include #include @@ -49,6 +51,274 @@ void verify(const T& t, const _CharT* str) VERIFY( res == str ); } +template +struct Rep +{ + using Return + = std::conditional_t, Rep, Ret>; + + Rep(long v = 0) : val(v) {} + + operator long() const + { return val; } + + Return + operator+() const + { return val; } + + Rep + operator-() const + { return -val; } + + friend Rep + operator+(Rep lhs, Rep rhs) + { return lhs.val + rhs.val; } + + friend Rep + operator-(Rep lhs, Rep rhs) + { return lhs.val - rhs.val; } + + friend Rep + operator*(Rep lhs, Rep rhs) + { return lhs.val * rhs.val; } + + friend Rep + operator/(Rep lhs, Rep rhs) + { return lhs.val / rhs.val; } + + friend auto operator<=>(Rep, Rep) = default; + + template + friend std::basic_ostream<_CharT>& + operator<<(std::basic_ostream<_CharT>& os, const Rep& t) + { return os << t.val << WIDEN("[via <<]"); } + + long val; +}; + +template + requires std::is_integral_v +struct std::common_type, Other> +{ + using type = Rep; +}; + +template + requires std::is_integral_v +struct std::common_type> + : std::common_type, Other> +{ }; + +template +struct std::numeric_limits> + : std::numeric_limits +{ }; + +template +struct std::formatter, _CharT> + : std::formatter +{ + template + typename std::basic_format_context::iterator + format(const Rep& t, std::basic_format_context& ctx) const + { + constexpr std::basic_string_view<_CharT> suffix = WIDEN("[via format]"); + auto out = std::formatter::format(t.val, ctx); + return std::ranges::copy(suffix, out).out; + } +}; + +using deciseconds = duration; + +template +void +test_duration() +{ + std::basic_string<_CharT> res; + + const milliseconds di(40); + verify( di, WIDEN("40ms") ); + res = std::format(WIDEN("{:>6}"), di); + VERIFY( res == WIDEN(" 40ms") ); + + verify( -di, WIDEN("-40ms") ); + res = std::format(WIDEN("{:>6}"), -di); + VERIFY( res == WIDEN(" -40ms") ); + + const duration df(11.22); + verify( df, WIDEN("11.22s") ); + res = std::format(WIDEN("{:=^12}"), df); + VERIFY( res == WIDEN("===11.22s===") ); + + verify( -df, WIDEN("-11.22s") ); + res = std::format(WIDEN("{:=^12}"), -df); + VERIFY( res == WIDEN("==-11.22s===") ); +} + +template +void +test_duration_cust() +{ + std::basic_string<_CharT> res; + const duration> charRep(123); + verify( charRep, WIDEN("123ds") ); + + // +asLong returns long, so formatted as long + const duration> asLong(20); + verify( asLong, WIDEN("20s") ); + res = std::format(WIDEN("{:>6}"), asLong); + VERIFY( res == WIDEN(" 20s") ); + + verify( -asLong, WIDEN("-20s") ); + res = std::format(WIDEN("{:>6}"), -asLong); + VERIFY( res == WIDEN(" -20s") ); + + res = std::format(WIDEN("{:%Q}"), asLong); + VERIFY( res == WIDEN("20") ); + res = std::format(WIDEN("{:+<7%Q}"), asLong); + VERIFY( res == WIDEN("20+++++") ); + + // +asRep returns Rep<>, so formatted as Rep<> + const duration> asRep(10); + verify( asRep, WIDEN("10[via <<]s") ); + res = std::format(WIDEN("{:=^15}"), asRep); + VERIFY( res == WIDEN("==10[via <<]s==") ); + + verify( -asRep, WIDEN("-10[via <<]s") ); + res = std::format(WIDEN("{:=^15}"), -asRep); + VERIFY( res == WIDEN("=-10[via <<]s==") ); + + res = std::format(WIDEN("{:%Q}"), asRep); + VERIFY( res == WIDEN("10[via format]") ); + res = std::format(WIDEN("{:=^18%Q}"), asRep); + VERIFY( res == WIDEN("==10[via format]==") ); + + const duration, std::milli> milliRep(10); + verify( milliRep, WIDEN("10[via <<]ms") ); + res = std::format(WIDEN("{:=^15}"), milliRep); + VERIFY( res == WIDEN("=10[via <<]ms==") ); + + verify( -milliRep, WIDEN("-10[via <<]ms") ); + res = std::format(WIDEN("{:=^15}"), -milliRep); + VERIFY( res == WIDEN("=-10[via <<]ms=") ); + + res = std::format(WIDEN("{:%Q}"), milliRep); + VERIFY( res == WIDEN("10[via format]") ); + res = std::format(WIDEN("{:=^18%Q}"), milliRep); + VERIFY( res == WIDEN("==10[via format]==") ); +} + +template +constexpr auto +hms(const duration& d) +{ + using Dur = duration; + return hh_mm_ss(duration_cast(d)); +} + +template +void +test_hh_mm_ss() +{ + auto dt = 22h + 24min + 54s + 111222333ns; + verify( hms(dt), + WIDEN("22:24:54.111222333") ); + verify( hms(dt), + WIDEN("22:24:54.111222") ); + verify( hms(dt), + WIDEN("22:24:54.111") ); + verify( hms(dt), + WIDEN("22:24:54.1") ); + verify( hms(dt), + WIDEN("22:24:54") ); + verify( hms(dt), + WIDEN("22:24:00") ); + verify( hms(dt), + WIDEN("22:00:00") ); + verify( hms(-dt), + WIDEN("-22:24:54.111222333") ); + verify( hms(-dt), + WIDEN("-22:24:54.111222") ); + verify( hms(-dt), + WIDEN("-22:24:54.111") ); + verify( hms(-dt), + WIDEN("-22:24:54.1") ); + verify( hms(-dt), + WIDEN("-22:24:54") ); + verify( hms(-dt), + WIDEN("-22:24:00") ); + verify( hms(-dt), + WIDEN("-22:00:00") ); + + verify( hms(-dt), + WIDEN("-22:24:54.111222333") ); + + dt += 300h; + verify( hms(dt), + WIDEN("322:24:54.111222333") ); + verify( hms(-dt), + WIDEN("-322:24:54.111222333") ); + + dt += 14000h; + verify( hms(dt), + WIDEN("14322:24:54.111222333") ); + verify( hms(-dt), + WIDEN("-14322:24:54.111222333") ); +} + +template +void +test_hh_mm_ss_cust() +{ + const duration charRep(123); + verify( hms(charRep), + WIDEN("00:00:12.3") ); + verify( hms(charRep), + WIDEN("00:00:12") ); + + auto dt = 22h + 24min + 54s + 123ms; + // +plus returns long, so formatted as long + const duration, std::milli> asLong(dt.count()); + verify( hms(asLong), + WIDEN("22:24:54.123[via format]") ); + verify( hms(asLong), + WIDEN("22:24:54.1[via format]") ); + verify( hms(asLong), + WIDEN("22:24:54") ); + verify( hms(-asLong), + WIDEN("-22:24:54.123[via format]") ); + verify( hms(-asLong), + WIDEN("-22:24:54.1[via format]") ); + verify( hms(-asLong), + WIDEN("-22:24:54") ); + + // +asRep returns Rep<>, so formatted as Rep<> + const duration, std::milli> asRep(dt.count()); + verify( hms(asRep), + WIDEN("22:24:54.123[via format]") ); + verify( hms(asRep), + WIDEN("22:24:54.1[via format]") ); + verify( hms(asLong), + WIDEN("22:24:54") ); + verify( hms(-asLong), + WIDEN("-22:24:54.123[via format]") ); + verify( hms(-asLong), + WIDEN("-22:24:54.1[via format]") ); + verify( hms(-asLong), + WIDEN("-22:24:54") ); +} + +template +void +test_durations() +{ + test_duration(); + test_duration_cust(); + + test_hh_mm_ss(); + test_hh_mm_ss_cust(); +} + template void test_day() @@ -288,6 +558,7 @@ void test_all() { test_padding(); + test_durations(); test_calendar(); }