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. > >+// { 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 > > > > > >