On Mon, May 5, 2025 at 4:48 PM Tomasz Kamiński <tkami...@redhat.com> 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.
> ---
>
This would also be fixed by switching to using _Padding_sink instead of
_Str_sink,
and I think we should follow up with that change. However, this may be
something we may want
to backport to the previous release, this commit implements smaller more
targeted changes.
Tested (locally) on x86_64 linux. OK for the trunk? Do we want to backport
and, if so, how far back?


Tested on x86_64 linux? OK for the trunk? Also 15,14?

>  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 } }
> +// { dg-do run { target c++23 } }
> +// { dg-add-options no_pch }
> +// { 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
>
>

Reply via email to