On Wed, 4 Jun 2025, 10:27 Tomasz Kamiński, <tkami...@redhat.com> wrote:
> 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. > --- > Tested on x86_64-linux. OK for trunk? > OK, thanks. The fix for the missing argument should be backported too. > libstdc++-v3/include/bits/chrono_io.h | 3 +- > .../testsuite/std/time/format/empty_spec.cc | 271 ++++++++++++++++++ > 2 files changed, 273 insertions(+), 1 deletion(-) > > 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 <chrono> > +#include <ranges> > #include <sstream> > #include <testsuite_hooks.h> > > @@ -49,6 +51,274 @@ void verify(const T& t, const _CharT* str) > VERIFY( res == str ); > } > > +template<typename Ret = void> > +struct Rep > +{ > + using Return > + = std::conditional_t<std::is_void_v<Ret>, 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<typename _CharT> > + friend std::basic_ostream<_CharT>& > + operator<<(std::basic_ostream<_CharT>& os, const Rep& t) > + { return os << t.val << WIDEN("[via <<]"); } > + > + long val; > +}; > + > +template<typename Ret, typename Other> > + requires std::is_integral_v<Other> > +struct std::common_type<Rep<Ret>, Other> > +{ > + using type = Rep<Ret>; > +}; > + > +template<typename Ret, typename Other> > + requires std::is_integral_v<Other> > +struct std::common_type<Other, Rep<Ret>> > + : std::common_type<Rep<Ret>, Other> > +{ }; > + > +template<typename Ret> > +struct std::numeric_limits<Rep<Ret>> > + : std::numeric_limits<long> > +{ }; > + > +template<typename Ret, typename _CharT> > +struct std::formatter<Rep<Ret>, _CharT> > + : std::formatter<long, _CharT> > +{ > + template<typename Out> > + typename std::basic_format_context<Out, _CharT>::iterator > + format(const Rep<Ret>& t, std::basic_format_context<Out, _CharT>& ctx) > const > + { > + constexpr std::basic_string_view<_CharT> suffix = WIDEN("[via > format]"); > + auto out = std::formatter<long, _CharT>::format(t.val, ctx); > + return std::ranges::copy(suffix, out).out; > + } > +}; > + > +using deciseconds = duration<seconds::rep, std::deci>; > + > +template<typename _CharT> > +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<double> 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<typename _CharT> > +void > +test_duration_cust() > +{ > + std::basic_string<_CharT> res; > + const duration<char, std::ratio<1, 10>> charRep(123); > + verify( charRep, WIDEN("123ds") ); > + > + // +asLong returns long, so formatted as long > + const duration<Rep<long>> 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<Rep<>> 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<Rep<>, 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<typename Ratio, typename Rep, typename Period> > +constexpr auto > +hms(const duration<Rep, Period>& d) > +{ > + using Dur = duration<Rep, typename Ratio::period>; > + return hh_mm_ss<Dur>(duration_cast<Dur>(d)); > +} > + > +template<typename _CharT> > +void > +test_hh_mm_ss() > +{ > + auto dt = 22h + 24min + 54s + 111222333ns; > + verify( hms<nanoseconds>(dt), > + WIDEN("22:24:54.111222333") ); > + verify( hms<microseconds>(dt), > + WIDEN("22:24:54.111222") ); > + verify( hms<milliseconds>(dt), > + WIDEN("22:24:54.111") ); > + verify( hms<deciseconds>(dt), > + WIDEN("22:24:54.1") ); > + verify( hms<seconds>(dt), > + WIDEN("22:24:54") ); > + verify( hms<minutes>(dt), > + WIDEN("22:24:00") ); > + verify( hms<hours>(dt), > + WIDEN("22:00:00") ); > + verify( hms<nanoseconds>(-dt), > + WIDEN("-22:24:54.111222333") ); > + verify( hms<microseconds>(-dt), > + WIDEN("-22:24:54.111222") ); > + verify( hms<milliseconds>(-dt), > + WIDEN("-22:24:54.111") ); > + verify( hms<deciseconds>(-dt), > + WIDEN("-22:24:54.1") ); > + verify( hms<seconds>(-dt), > + WIDEN("-22:24:54") ); > + verify( hms<minutes>(-dt), > + WIDEN("-22:24:00") ); > + verify( hms<hours>(-dt), > + WIDEN("-22:00:00") ); > + > + verify( hms<nanoseconds>(-dt), > + WIDEN("-22:24:54.111222333") ); > + > + dt += 300h; > + verify( hms<nanoseconds>(dt), > + WIDEN("322:24:54.111222333") ); > + verify( hms<nanoseconds>(-dt), > + WIDEN("-322:24:54.111222333") ); > + > + dt += 14000h; > + verify( hms<nanoseconds>(dt), > + WIDEN("14322:24:54.111222333") ); > + verify( hms<nanoseconds>(-dt), > + WIDEN("-14322:24:54.111222333") ); > +} > + > +template<typename _CharT> > +void > +test_hh_mm_ss_cust() > +{ > + const duration<char, deciseconds::period> charRep(123); > + verify( hms<deciseconds>(charRep), > + WIDEN("00:00:12.3") ); > + verify( hms<seconds>(charRep), > + WIDEN("00:00:12") ); > + > + auto dt = 22h + 24min + 54s + 123ms; > + // +plus returns long, so formatted as long > + const duration<Rep<long>, std::milli> asLong(dt.count()); > + verify( hms<milliseconds>(asLong), > + WIDEN("22:24:54.123[via format]") ); > + verify( hms<deciseconds>(asLong), > + WIDEN("22:24:54.1[via format]") ); > + verify( hms<seconds>(asLong), > + WIDEN("22:24:54") ); > + verify( hms<milliseconds>(-asLong), > + WIDEN("-22:24:54.123[via format]") ); > + verify( hms<deciseconds>(-asLong), > + WIDEN("-22:24:54.1[via format]") ); > + verify( hms<seconds>(-asLong), > + WIDEN("-22:24:54") ); > + > + // +asRep returns Rep<>, so formatted as Rep<> > + const duration<Rep<>, std::milli> asRep(dt.count()); > + verify( hms<milliseconds>(asRep), > + WIDEN("22:24:54.123[via format]") ); > + verify( hms<deciseconds>(asRep), > + WIDEN("22:24:54.1[via format]") ); > + verify( hms<seconds>(asLong), > + WIDEN("22:24:54") ); > + verify( hms<milliseconds>(-asLong), > + WIDEN("-22:24:54.123[via format]") ); > + verify( hms<deciseconds>(-asLong), > + WIDEN("-22:24:54.1[via format]") ); > + verify( hms<seconds>(-asLong), > + WIDEN("-22:24:54") ); > +} > + > +template<typename CharT> > +void > +test_durations() > +{ > + test_duration<CharT>(); > + test_duration_cust<CharT>(); > + > + test_hh_mm_ss<CharT>(); > + test_hh_mm_ss_cust<CharT>(); > +} > + > template<typename _CharT> > void > test_day() > @@ -288,6 +558,7 @@ void > test_all() > { > test_padding<CharT>(); > + test_durations<CharT>(); > test_calendar<CharT>(); > } > > -- > 2.49.0 > >