On Tue, 6 May 2025 at 13:06, Tomasz Kaminski <tkami...@redhat.com> wrote: > > > > On Tue, May 6, 2025 at 1:59 PM Jonathan Wakely <jwak...@redhat.com> wrote: >> >> On 05/05/25 16:45 +0200, Tomasz Kamiński wrote: >> >Use `__unicode::_field_width` to compute the field width of the output when >> >writting >> >the formatted output for std::chrono::types. This applies both to >> >characters copied >> >from format string, and one produced by localized formatting. >> > >> >We also use _Str_sink::view() instead of get(), which avoids copying the >> >content of >> >the buffer to std::string in case of small output. >> > >> > PR libstdc++/120114 >> > >> >libstdc++-v3/ChangeLog: >> > >> > * include/bits/chrono_io.h (__formatter_chrono::_M_format): Use >> > __field_width. >> > * testsuite/std/time/format/pr120114.cc: New test. >> >--- >> > libstdc++-v3/include/bits/chrono_io.h | 9 +- >> > .../testsuite/std/time/format/pr120114.cc | 127 ++++++++++++++++++ >> > 2 files changed, 134 insertions(+), 2 deletions(-) >> > create mode 100644 libstdc++-v3/testsuite/std/time/format/pr120114.cc >> > >> >diff --git a/libstdc++-v3/include/bits/chrono_io.h >> >b/libstdc++-v3/include/bits/chrono_io.h >> >index b7f6f5f49e5..620227a9f35 100644 >> >--- a/libstdc++-v3/include/bits/chrono_io.h >> >+++ b/libstdc++-v3/include/bits/chrono_io.h >> >@@ -705,8 +705,13 @@ namespace __format >> > if (__write_direct) >> > return __out; >> > >> >- auto __str = std::move(__sink).get(); >> >- return __format::__write_padded_as_spec(__str, __str.size(), >> >+ auto __str = __sink.view(); >> >+ size_t __width; >> >+ if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) >> >+ __width = __unicode::__field_width(__str); >> >+ else >> >+ __width = __str.size(); >> >+ return __format::__write_padded_as_spec(__str, __width, >> > __fc, _M_spec); >> > } >> > >> >diff --git a/libstdc++-v3/testsuite/std/time/format/pr120114.cc >> >b/libstdc++-v3/testsuite/std/time/format/pr120114.cc >> >new file mode 100644 >> >index 00000000000..08968eb053a >> >--- /dev/null >> >+++ b/libstdc++-v3/testsuite/std/time/format/pr120114.cc >> >@@ -0,0 +1,127 @@ >> >+// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE >> >-DUNICODE_ENC" { target le } } >> >+// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32BE >> >-DUNICODE_ENC" { target be } } >> >> Do we need the -DUNICODE_ENC options? The macro isn't used in the >> test. >> >> Do we even need the exec charset options here? > > I believe "\u32C8" is ill-formed when the exec-charset is not UTF-8, so I > think "-fexec-charset=UTF-8" is necessary.
UTF-8 is the default so it would only fail if people intentionally run the tests with some other -fexec-charset option, but I do occasionally do that to test things with e.g. -fexec-charset=ascii so let's keep the -fexec-charset > >> >> >+// { dg-do run { target c++23 } } >> >+// { dg-add-options no_pch } >> >> I don't think we need no_pch here either. >> >> >+// { dg-timeout-factor 2 } >> >+ >> >+#include <algorithm> >> >+#include <chrono> >> >+#include <testsuite_hooks.h> >> >+ >> >+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) >> >+#define WIDEN(S) WIDEN_(_CharT, S) >> >+ >> >+template<typename _CharT> >> >+void >> >+test_from_format_string() >> >+{ >> >+ std::basic_string<_CharT> res; >> >+ using namespace std::chrono_literals; >> >+ auto date = 2025y/std::chrono::May/05d; >> >+ >> >+ res = std::format(WIDEN("{:+<13%F\U0001f921}"), date); >> >+ VERIFY( res == WIDEN("2025-05-05\U0001f921+") ); >> >+ >> >+ res = std::format(WIDEN("{:->15%F\U0001f921}"), date); >> >+ VERIFY( res == WIDEN("---2025-05-05\U0001f921") ); >> >+ >> >+ res = std::format(WIDEN("{:=^20%F\U0001f921}"), date); >> >+ VERIFY( res == WIDEN("====2025-05-05\U0001f921====") ); >> >+} >> >+ >> >+template<typename _CharT> >> >+void >> >+test_formatted_value() >> >+{ >> >+ // Custom time_put facet which returns Ideographic Telegraph Symbol >> >+ // for given month for Om. >> >+ struct TimePut : std::time_put<_CharT> >> >+ { >> >+ using iter_type = std::time_put<_CharT>::iter_type; >> >+ using char_type = std::time_put<_CharT>::char_type; >> >+ >> >+ iter_type >> >+ do_put(iter_type out, std::ios_base& io, char_type fill, const tm* t, >> >+ char format, char modifier) const override >> >+ { >> >+ if (format != 'm' && modifier != 'm') >> >+ return std::time_put<_CharT>::do_put(out, io, fill, t, format, >> >modifier); >> >+ std::basic_string_view<_CharT> str; >> >+ switch (t->tm_mon) >> >+ { >> >+ case 0: >> >+ str = WIDEN("\u32C0"); >> >+ break; >> >+ case 1: >> >+ str = WIDEN("\u32C1"); >> >+ break; >> >+ case 2: >> >+ str = WIDEN("\u32C2"); >> >+ break; >> >+ case 3: >> >+ str = WIDEN("\u32C3"); >> >+ break; >> >+ case 4: >> >+ str = WIDEN("\u32C4"); >> >+ break; >> >+ case 5: >> >+ str = WIDEN("\u32C5"); >> >+ break; >> >+ case 6: >> >+ str = WIDEN("\u32C6"); >> >+ break; >> >+ case 7: >> >+ str = WIDEN("\u32C7"); >> >+ break; >> >+ case 8: >> >+ str = WIDEN("\u32C8"); >> >+ break; >> >+ case 9: >> >+ str = WIDEN("\u32C9"); >> >+ break; >> >+ case 10: >> >+ str = WIDEN("\u32CA"); >> >+ break; >> >+ case 11: >> >+ str = WIDEN("\u32CB"); >> >+ break; >> >+ }; >> >+ return std::copy(str.begin(), str.end(), out); >> >+ } >> >+ }; >> >+ const std::locale loc(std::locale::classic(), new TimePut); >> >+ >> >+ std::basic_string<_CharT> res; >> >+ >> >+ res = std::format(loc, WIDEN("{:<1L%Om}"), std::chrono::January); >> >+ VERIFY( res == WIDEN("\u32C0") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:>2L%Om}"), std::chrono::February); >> >+ VERIFY( res == WIDEN("\u32C1") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:<3L%Om}"), std::chrono::March); >> >+ VERIFY( res == WIDEN("\u32C2 ") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:^4L%Om}"), std::chrono::April); >> >+ VERIFY( res == WIDEN(" \u32C3 ") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:>5L%Om}"), std::chrono::May); >> >+ VERIFY( res == WIDEN(" \u32C4") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:+<6L%Om}"), std::chrono::June); >> >+ VERIFY( res == WIDEN("\u32C5++++") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:=^7L%Om}"), std::chrono::July); >> >+ VERIFY( res == WIDEN("==\u32C6===") ); >> >+ >> >+ res = std::format(loc, WIDEN("{:->8L%Om}"), std::chrono::August); >> >+ VERIFY( res == WIDEN("------\u32C7") ); >> >+} >> >+ >> >+int main() >> >+{ >> >+ test_from_format_string<char>(); >> >+ test_from_format_string<wchar_t>(); >> >+ test_formatted_value<char>(); >> >+ test_formatted_value<wchar_t>(); >> >+} >> >-- >> >2.49.0 >> > >> > >>