[PATCH] libstdc++: Fix format call and test formatting with empty specs for durations.
Jonathan Wakely
jwakely.gcc@gmail.com
Wed Jun 4 10:38:10 GMT 2025
On Wed, 4 Jun 2025, 10:27 Tomasz Kamiński, <tkaminsk@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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250604/f199f004/attachment-0001.htm>
More information about the Libstdc++
mailing list