From patchwork Wed Oct 2 19:36:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1992146 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=CQiTq8tm; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XJlS93zlBz1xtY for ; Thu, 3 Oct 2024 05:36:56 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2A4D5385DDDC for ; Wed, 2 Oct 2024 19:36:54 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id DE16F3858D34 for ; Wed, 2 Oct 2024 19:36:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DE16F3858D34 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org DE16F3858D34 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727897791; cv=none; b=u4VNUJoYANzp62R3i6yUPTdkv142Uyw+vKQH4nQM6vj4krNjjKV+LCH08pgSLYEc0p++MC1r7jWzN1DVzCDxkq12OMGptODyr8rbM1JAMNqO4OSzxuLF8+KmRlJ0+aFb5pHR3lJv+2w6c8oUv2iI4bnoByJhYeO0sgbdyznMBVw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727897791; c=relaxed/simple; bh=SJSW7+nUFZEcosSbllmUY2fpoMKHPGtzkr1Kbs0i3Jk=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ViMR0E+kZFa2oqUnfw43wJki3E446GJpD6YeFk835jussQ948LDFEvxzUxUCElZwGmbQ2m38pepCeJXRq9vm1tZaLzAnGVjLXrWLBI9It/ft9DJEBlros4/nQPEPST4lbkTE3ToJEJzlVfcRdesr8M6iLqUxg+R1S5+yJfKpawY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727897789; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Cyd7fJ6OTj9Q7WWxqE6zn3cSQX8HL0JzmB+7VY96E58=; b=CQiTq8tmJpj6o+wYaSMIarbzCjvXEkxxgT+sKWXXVv7OkpkHV8OnAcXjZ0SLrKX1NvMir1 qcPCx4QyEqzuIRnbkC5Bc4iIRdQoXHw/x1i17gOste4vq5d8ZaaCkjMNcJTbapceKmL8b0 wTwOktZ3NSUVYm5EnN+n4PvbH4BHW+I= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-626-tcgub9udNBmZAELTK6lINQ-1; Wed, 02 Oct 2024 15:36:26 -0400 X-MC-Unique: tcgub9udNBmZAELTK6lINQ-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 445C91955EA8; Wed, 2 Oct 2024 19:36:25 +0000 (UTC) Received: from localhost (unknown [10.42.28.136]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 015A11956086; Wed, 2 Oct 2024 19:36:23 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH] libstdc++: Fix formatting of chrono::duration with character rep [PR116755] Date: Wed, 2 Oct 2024 20:36:02 +0100 Message-ID: <20241002193622.3359618-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- Implement Peter Dimov's suggestion for resolving LWG 4118, which is to use +d.count() so that character types are promoted to an integer type before formatting them. This didn't have unanimous consensus in the committee as Howard Hinnant proposed that we should format the rep consistently with std::format("{}", d.count()) instead. That ends up being more complicated, because it makes std::formattable a precondition of operator<< which was not previously the case, and it means that ios_base::fmtflags from the stream would be ignored because std::format doesn't use them. libstdc++-v3/ChangeLog: PR libstdc++/116755 * include/bits/chrono_io.h (operator<<): Use +d.count() for duration inserter. (__formatter_chrono::_M_format): Likewise for %Q format. * testsuite/20_util/duration/io.cc: Test durations with character types as reps. --- libstdc++-v3/include/bits/chrono_io.h | 9 ++- libstdc++-v3/testsuite/20_util/duration/io.cc | 66 +++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 362bb5aa9e9..a337007266e 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -150,7 +150,9 @@ namespace __detail __s.flags(__os.flags()); __s.imbue(__os.getloc()); __s.precision(__os.precision()); - __s << __d.count(); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4118. How should duration formatters format custom rep types? + __s << +__d.count(); __detail::__fmt_units_suffix(_Out(__s)); __os << std::move(__s).str(); return __os; @@ -635,8 +637,10 @@ namespace __format case 'Q': // %Q The duration's numeric value. if constexpr (chrono::__is_duration_v<_Tp>) + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4118. How should duration formatters format custom rep? __out = std::format_to(__print_sign(), _S_empty_spec, - __t.count()); + +__t.count()); else __throw_format_error("chrono format error: argument is " "not a duration"); @@ -1703,6 +1707,7 @@ namespace __format /// @endcond template + requires __format::__formattable_impl<_Rep, _CharT> struct formatter, _CharT> { constexpr typename basic_format_parse_context<_CharT>::iterator diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc index 57020f4f953..383fb60afe2 100644 --- a/libstdc++-v3/testsuite/20_util/duration/io.cc +++ b/libstdc++-v3/testsuite/20_util/duration/io.cc @@ -23,6 +23,24 @@ test01() VERIFY( s == "3[2]s" ); std::getline(ss, s); VERIFY( s == "9[2/3]s" ); + + // LWG 4118. How should duration formatters format custom rep types? + ss.str(""); + ss << duration(121) << ' '; + ss << duration(122) << ' '; + ss << duration(123) << ' '; + ss << duration(124) << ' '; + ss << duration(125) << ' '; + ss << duration(126) << ' '; + ss << duration(127) << ' '; + VERIFY( ss.str() == "121s 122s 123s 124s 125s 126s 127s " ); + + ss.str(""); + ss << std::hex << std::uppercase << duration(0x1A) << ' '; + ss << std::hex << std::uppercase << duration(0x2A) << ' '; + ss << std::hex << std::uppercase << duration(0x3A) << ' '; + ss << std::scientific << duration(4.5) << ' '; + VERIFY( ss.str() == "1As 2As 3As 4.500000E+00s " ); } void @@ -44,6 +62,24 @@ test02() VERIFY( s == L"3[2]s" ); std::getline(ss, s); VERIFY( s == L"9[2/3]s" ); + + // LWG 4118. How should duration formatters format custom rep types? + ss.str(L""); + ss << duration(121) << ' '; + ss << duration(122) << ' '; + ss << duration(123) << ' '; + ss << duration(124) << ' '; + ss << duration(125) << ' '; + ss << duration(126) << ' '; + ss << duration(127) << ' '; + VERIFY( ss.str() == L"121s 122s 123s 124s 125s 126s 127s " ); + + ss.str(L""); + ss << std::hex << std::uppercase << duration(0x1A) << ' '; + ss << std::hex << std::uppercase << duration(0x2A) << ' '; + ss << std::hex << std::uppercase << duration(0x3A) << ' '; + ss << std::scientific << duration(4.5) << ' '; + VERIFY( ss.str() == L"1As 2As 3As 4.500000E+00s " ); #endif } @@ -114,6 +150,36 @@ test_format() VERIFY( s == expected ); s = std::format("{:%Q%q}", minsec); VERIFY( s == expected ); + + // LWG 4118. How should duration formatters format custom rep types? + s = std::format("{}", std::chrono::duration(100)); + VERIFY( s == "100s" ); + s = std::format("{:%Q}", std::chrono::duration(101)); + VERIFY( s == "101" ); +#ifdef _GLIBCXX_USE_WCHAR_T + ws = std::format(L"{}", std::chrono::duration(102)); + VERIFY( ws == L"102s" ); + ws = std::format(L"{}", std::chrono::duration(103)); + VERIFY( ws == L"103s" ); +#endif + s = std::format("{}", std::chrono::duration(50)); + VERIFY( s == "50s" ); + s = std::format("{:%Q}", std::chrono::duration(51)); + VERIFY( s == "51" ); + s = std::format("{}", std::chrono::duration(52)); + VERIFY( s == "52s" ); + s = std::format("{:%Q}", std::chrono::duration(53)); + VERIFY( s == "53" ); + +#if __cplusplus > 202002L + static_assert( ! std::formattable, char> ); + static_assert( ! std::formattable, char> ); + static_assert( ! std::formattable, char> ); + static_assert( ! std::formattable, char> ); + static_assert( ! std::formattable, wchar_t> ); + static_assert( ! std::formattable, wchar_t> ); + static_assert( ! std::formattable, wchar_t> ); +#endif } void