From 4b3cefed1a08344495fedec4982d85168bd8173f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kami=C5=84ski?= Date: Tue, 24 Jun 2025 14:07:46 +0200 Subject: libstdc++: Type-erase chrono-data for formatting [PR110739] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch reworks the formatting for the chrono types, such that they are all formatted in terms of _ChronoData class, that includes all required fields. Populating each required field is performed in formatter for specific type, based on the chrono-spec used. To facilitate above, the _ChronoSpec now includes additional _M_needed field, that represnts the chrono data that is referenced by format spec (this value is also configured for __defSpec). This value differs from the value of __parts passed to _M_parse, which does include all fields that can be computed from input (e.g. weekday_indexed can be computed for year_month_day). Later it is used to fill _ChronoData, in particular _M_fill_* family of functions, to determine if given field needs to be set, and thus its value needs to be computed. In consequence _ChronoParts enum was extended with additional values, that allows more fine grained identification: * _TimeOfDay is separated into _HoursMinutesSeconds and _Subseconds, * _TimeZone is separated into _ZoneAbbrev and _ZoneOffset, * _LocalDays, _WeekdayIndex are defined and in included in _Date, * _Duration is removed, and instead _EpochUnits and _UnitSuffix are introduced. Furthermore, to avoid name conflicts _ChonoParts is now defined as enum class, with additional operators that simplify uses. In addition to fields that can be printed using chrono-spec, _ChronoData stores: * Total days in wall time (_M_ldays), day of year (_M_day_of_year) - used by struct tm construction, and for ISO calendar computation. * Total seconds in wall time (_M_lseconds) - this value may be different from sum of days, hours, minutes, seconds (e.g. see utc_time below). Included to allow future extension, like printing total minutes. * Total seconds since epoch - due offset different from above. Again to be used with future extension (e.g. %s as proposed in P2945R1). * Subseconds - count of attoseconds (10^(-18)), in addition to printing can be used to compute fractional hours, minutes. The both total seconds fields use single _TotalSeconds enumerator in _ChronoParts, that when present in combination with _EpochUnits or _LocalDays indicates that _M_eseconds (_EpochSeconds) or _M_lseconds (_LocalSeconds) are provided/required. To handle type formatting of time since epoch ('%Q'|_EpochUnits), we use the format_args mechanism, where the result of +d.count() (see LWG4118) is erased into make_format_args to local __arg_store, that is later referenced by _M_ereps (_M_ereps.get(0)). To handle precision values, and in prepartion to allow user to configure ones, we store the precision as third element of _M_ereps (_M_ereps.get(2)), this allows duration with precision to be printed using "{0:{2}}". For subseconds the precision is handled differently depending on the representation: * for integral reps, _M_subseconds value is used to determine fractional value, precision is trimmed to 18 digits; * for floating-points, _M_ereps stores duration initialized with only fractional seconds, that is later formatted with precision. Always using _M_subseconds fields for integral duration, means that we do not use formattter for user-defined durations that are considered to be integral (see empty_spec.cc file change). To avoid potentially expensive computation of _M_subseconds, we make sure that _ChronoParts::_Subseconds is set only if _Subseconds are needed. In particular we remove this flag for localized ouput in _M_parse. Construction of the _M_ereps as described above is handled by __formatter_duration, that is then used to format duration, hh_mm_ss and time_points specializations. This class also handles _UnitSuffix, the _M_units_suffix field is populated either with predefined suffix (chrono::__detail::__units_suffix) or one produced locally. Finally, formatters for types listed below contains type specific logic: * hh_mm_ss - we do not compute total duration and seconds, unless explicitly requested, as such computation may overflow; * utc_time - for time during leap second insertion, the _M_seconds field is increased to 60; * __local_time_fmt - exception is thrown if zone offset (_ZoneOffset) or abbrevation (_ZoneAbbrev) is requsted, but corresponding pointer is null, futhermore conversion from `char` to `wchar_t` for abbreviation is performed if needed. PR libstdc++/110739 libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__format::__no_timezone_available): Removed, replaced with separate throws in formatter for __local_time_fmt (__format::_ChronoParts): Defined additional enumertors and declared as enum class. (__format::operator&(_ChronoParts, _ChronoParts)) (__format::operator&=(_ChronoParts&, _ChronoParts)) (__format::operator-(_ChronoParts, _ChronoParts)) (__format::operator-=(_ChronoParts&, _ChronoParts)) (__format::operator==(_ChronoParts, decltype(nullptr))) (_ChronoSpec::_M_time_only, _ChronoSpec::_M_floating_point_rep) (_ChronoSpec::_M_custom_rep, _ChronoSpec::_M_needed) (_ChronoSpec::_M_needs, __format::_ChronoData): Define. (__format::__formatter_chrono): Redefine to accept _ChronoData. (__formatter_chrono::_M_format_to_ostream): Moved to __formatter_duration. (__format::__formatter_duration): Define. (__formatter_chrono_info::format): Pass value-constructed _ChronoData. (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter) (std::formatter): Construct _ChronoData in format, and configure _M_needed in _ChronoSpec. (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>) (std::formatter, _CharT>): Reworked in terms of __formatter_duration and _ChronoData. (std::formatter, _CharT>): Removed. (_Parser<_Duration>::operator()): Adjusted for _ChronoParts being enum class. * include/std/chrono (__detail::__utc_leap_second): Removed, replaced with simply bumping _M_seconds in _ChronoData. * testsuite/std/time/format/empty_spec.cc: Updated %S integral ouput. Reviewed-by: Jonathan Wakely Signed-off-by: Tomasz KamiƄski --- libstdc++-v3/testsuite/std/time/format/empty_spec.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'libstdc++-v3/testsuite/std') diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc index 5f7b868..923df3d 100644 --- a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc +++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc @@ -355,30 +355,30 @@ test_hh_mm_ss_cust() // +plus returns long, so formatted as long const duration, std::milli> asLong(dt.count()); verify( hms(asLong), - WIDEN("22:24:54.123[via format]") ); + WIDEN("22:24:54.123") ); verify( hms(asLong), - WIDEN("22:24:54.1[via format]") ); + WIDEN("22:24:54.1") ); verify( hms(asLong), WIDEN("22:24:54") ); verify( hms(-asLong), - WIDEN("-22:24:54.123[via format]") ); + WIDEN("-22:24:54.123") ); verify( hms(-asLong), - WIDEN("-22:24:54.1[via format]") ); + WIDEN("-22:24:54.1") ); 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]") ); + WIDEN("22:24:54.123") ); verify( hms(asRep), - WIDEN("22:24:54.1[via format]") ); + WIDEN("22:24:54.1") ); verify( hms(asLong), WIDEN("22:24:54") ); verify( hms(-asLong), - WIDEN("-22:24:54.123[via format]") ); + WIDEN("-22:24:54.123") ); verify( hms(-asLong), - WIDEN("-22:24:54.1[via format]") ); + WIDEN("-22:24:54.1") ); verify( hms(-asLong), WIDEN("-22:24:54") ); } -- cgit v1.1