On Tue, May 6, 2025 at 1:59 PM Jonathan Wakely <[email protected]> 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
> >
> >
>
>