From patchwork Sat Oct 15 20:25:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 2972 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:4ac7:0:0:0:0:0 with SMTP id y7csp772121wrs; Sat, 15 Oct 2022 13:26:17 -0700 (PDT) X-Google-Smtp-Source: AMsMyM5UEVD+52YFM/5vCCetNbkhqqrYOQFwx+JSaD0DLiLDnvUTIAxGTdE/MXHYvXkYkJjL2rGg X-Received: by 2002:a17:906:4fc3:b0:72e:eab4:d9d7 with SMTP id i3-20020a1709064fc300b0072eeab4d9d7mr3084781ejw.599.1665865576928; Sat, 15 Oct 2022 13:26:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1665865576; cv=none; d=google.com; s=arc-20160816; b=kU9U59IDhkl4I+0eZxDk7JJYyVBEyWnae+sjMs6R6aKUM8gASDwSqfAHl721ABRxs6 xB4jTLzVzfyrqj/FjvQtMgfscP/WdfCjdnW7BLQOdX8PRtBJ4Tva2CD8vQgxrLfFrjUS ke66nI0OQbYqhw8RB0YR4GtIyMQuL7OI/Bxx5627BaWqM9vqTSNZeVa8UYy1uj3atcJH FZTiJNQg4wDIanmqmHLYDWVS3km3AcDq9c5oQwFhoRR4ZomfV/H9NbMJ0WLq+gO9qfuM gkSxK3D/kZFOiP+eL6y/qmelRo/wwPBD2Dcqdj9jOWBt/64AVGigmZFNfzi/vk/RfyLY 5jzg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:to :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=bcunHjM+wE5qKbVLry4L3noWgBWTjqY1jqLkIfOd0fE=; b=LBhKxnlkxh0Tmi4gIS/wq26rlqGjBFnYli+RAcXQlTv9CG0rHR/4+py/LcnF62Itbj CHwtcy6X5N1POFhUrON5MC52YkRcXXBvz8SdGOTCIsN5rCUr975hFxSbYnZTied1buGJ lZ90PO8m02/gOxsc9GcZNfbG85EHiTe4MHGfkaRfs2oSJvFSkBbx1oJcvlo0I1m4W5FI wi7FHsD6tlU0zoUaveQMlDyRb+1+7xkrZX2LhiGJAZFqO0SkpcuZdghAfm8HJCWNmoGx 7hdvqBv4m2gDV24+rIU9wxrjudMToKfW1BUgFquUGbFiLNcTHqPTC01vAkyc8TRfoU+a LRbw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=gzu8EKGi; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id mp28-20020a1709071b1c00b0078e27f2ef4bsi3937014ejc.956.2022.10.15.13.26.16 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Oct 2022 13:26:16 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=gzu8EKGi; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E1BD63858427 for ; Sat, 15 Oct 2022 20:26:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E1BD63858427 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1665865575; bh=bcunHjM+wE5qKbVLry4L3noWgBWTjqY1jqLkIfOd0fE=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=gzu8EKGiAdNaos5b4tzyvXM4wePrCIMcSYYgTGE0jrOSje57vjHKjm8brs2klPYph DuqKRO9RX57jwFlpA1Xh8ETFAmb8Kw2qk5JPEWELXb427k1e/ZHXEEUXyI8XDwQsEo joILHLjAiuuADWYLM8Etrm19nXhpJixi0WulR95Q= 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 ESMTPS id 6A5FF3858CDA for ; Sat, 15 Oct 2022 20:25:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 6A5FF3858CDA Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-546-XHW8AbVgNsqSrY8DGZTsCg-1; Sat, 15 Oct 2022 16:25:20 -0400 X-MC-Unique: XHW8AbVgNsqSrY8DGZTsCg-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id AF4D080280D; Sat, 15 Oct 2022 20:25:19 +0000 (UTC) Received: from localhost (unknown [10.33.36.7]) by smtp.corp.redhat.com (Postfix) with ESMTP id 56F96535D29; Sat, 15 Oct 2022 20:25:19 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Implement constexpr std::to_chars for C++23 (P2291R3) Date: Sat, 15 Oct 2022 21:25:18 +0100 Message-Id: <20221015202518.2687700-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.10 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, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham 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.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1746786662699487061?= X-GMAIL-MSGID: =?utf-8?q?1746786662699487061?= This has been approved for C++23. I've just realised that the commit msg doesn't match the actual patch. I had a consteval lambda in an earlier version, but I changed it to do: +#if __cpp_lib_constexpr_charconv + if (std::__is_constant_evaluated()) + return __table<_DecOnly>.__data[__c]; +#endif So the code is unchanged for non-constexpr, and uses a global variable template for constexpr evaluations. If P2647R0 gets accepted and applies to C++23 mode then we can remove this and the static constexpr variable will work for both cases. Tested powerpc64le-linux. Pushed to trunk. -- >8 -- Some of the helper functions use static constexpr local variables, which is not permitted in a core constant expression. Removing the 'static' seems to have negligible performance effect for __to_chars and __to_chars_16. For __from_chars_alnum_to_val removing the 'static' causes a significant performance impact for base 36 conversions. Use a consteval lambda instead. libstdc++-v3/ChangeLog: * include/bits/charconv.h (__to_chars_10_impl): Add constexpr for C++23. Remove 'static' from array. * include/std/charconv (__cpp_lib_constexpr_charconv): Define. (__to_chars, __to_chars_16): Remove 'static' from array, add constexpr. (__to_chars_10, __to_chars_8, __to_chars_2, __to_chars_i) (to_chars, __raise_and_add, __from_chars_pow2_base) (__from_chars_alnum, from_chars): Add constexpr. (__from_chars_alnum_to_val): Avoid local static during constant evaluation. Add constexpr. * include/std/version (__cpp_lib_constexpr_charconv): Define. * testsuite/20_util/from_chars/constexpr.cc: New test. * testsuite/20_util/to_chars/constexpr.cc: New test. * testsuite/20_util/to_chars/version.cc: New test. --- libstdc++-v3/include/bits/charconv.h | 4 +- libstdc++-v3/include/std/charconv | 41 +++-- libstdc++-v3/include/std/version | 1 + .../testsuite/20_util/from_chars/constexpr.cc | 57 ++++++ .../testsuite/20_util/to_chars/constexpr.cc | 172 ++++++++++++++++++ .../testsuite/20_util/to_chars/version.cc | 16 ++ 6 files changed, 275 insertions(+), 16 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/from_chars/constexpr.cc create mode 100644 libstdc++-v3/testsuite/20_util/to_chars/constexpr.cc create mode 100644 libstdc++-v3/testsuite/20_util/to_chars/version.cc diff --git a/libstdc++-v3/include/bits/charconv.h b/libstdc++-v3/include/bits/charconv.h index 4cae10a72f7..d04aab77624 100644 --- a/libstdc++-v3/include/bits/charconv.h +++ b/libstdc++-v3/include/bits/charconv.h @@ -68,13 +68,13 @@ namespace __detail // The caller is required to provide a buffer of exactly the right size // (which can be determined by the __to_chars_len function). template - void + _GLIBCXX23_CONSTEXPR void __to_chars_10_impl(char* __first, unsigned __len, _Tp __val) noexcept { static_assert(is_integral<_Tp>::value, "implementation bug"); static_assert(is_unsigned<_Tp>::value, "implementation bug"); - static constexpr char __digits[201] = + constexpr char __digits[201] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv index 64d0584a55d..4b6cc83a567 100644 --- a/libstdc++-v3/include/std/charconv +++ b/libstdc++-v3/include/std/charconv @@ -50,6 +50,10 @@ # define __cpp_lib_to_chars 201611L #endif +#if __cplusplus > 202002L +# define __cpp_lib_constexpr_charconv 202202L +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -119,7 +123,7 @@ namespace __detail // Generic implementation for arbitrary bases. template - to_chars_result + constexpr to_chars_result __to_chars(char* __first, char* __last, _Tp __val, int __base) noexcept { static_assert(is_integral<_Tp>::value, "implementation bug"); @@ -138,7 +142,7 @@ namespace __detail unsigned __pos = __len - 1; - static constexpr char __digits[] = { + constexpr char __digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', @@ -160,7 +164,7 @@ namespace __detail } template - __integer_to_chars_result_type<_Tp> + constexpr __integer_to_chars_result_type<_Tp> __to_chars_16(char* __first, char* __last, _Tp __val) noexcept { static_assert(is_integral<_Tp>::value, "implementation bug"); @@ -177,7 +181,7 @@ namespace __detail return __res; } - static constexpr char __digits[] = { + constexpr char __digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; @@ -207,7 +211,7 @@ namespace __detail } template - inline __integer_to_chars_result_type<_Tp> + constexpr __integer_to_chars_result_type<_Tp> __to_chars_10(char* __first, char* __last, _Tp __val) noexcept { static_assert(is_integral<_Tp>::value, "implementation bug"); @@ -231,7 +235,7 @@ namespace __detail } template - __integer_to_chars_result_type<_Tp> + constexpr __integer_to_chars_result_type<_Tp> __to_chars_8(char* __first, char* __last, _Tp __val) noexcept { static_assert(is_integral<_Tp>::value, "implementation bug"); @@ -285,7 +289,7 @@ namespace __detail } template - __integer_to_chars_result_type<_Tp> + constexpr __integer_to_chars_result_type<_Tp> __to_chars_2(char* __first, char* __last, _Tp __val) noexcept { static_assert(is_integral<_Tp>::value, "implementation bug"); @@ -322,7 +326,7 @@ namespace __detail } // namespace __detail template - __detail::__integer_to_chars_result_type<_Tp> + constexpr __detail::__integer_to_chars_result_type<_Tp> __to_chars_i(char* __first, char* __last, _Tp __value, int __base = 10) { __glibcxx_assert(2 <= __base && __base <= 36); @@ -361,7 +365,7 @@ namespace __detail } #define _GLIBCXX_TO_CHARS(T) \ - inline to_chars_result \ + _GLIBCXX23_CONSTEXPR inline to_chars_result \ to_chars(char* __first, char* __last, T __value, int __base = 10) \ { return std::__to_chars_i(__first, __last, __value, __base); } _GLIBCXX_TO_CHARS(char) @@ -400,7 +404,7 @@ _GLIBCXX_TO_CHARS(unsigned __GLIBCXX_TYPE_INT_N_3) namespace __detail { template - bool + constexpr bool __raise_and_add(_Tp& __val, int __base, unsigned char __c) { if (__builtin_mul_overflow(__val, __base, &__val) @@ -429,18 +433,27 @@ namespace __detail return __table; } +#if __cpp_lib_constexpr_charconv + template + inline constexpr auto __table = __from_chars_alnum_to_val_table(); +#endif + // If _DecOnly is true: if the character is a decimal digit, then // return its corresponding base-10 value, otherwise return a value >= 127. // If _DecOnly is false: if the character is an alphanumeric digit, then // return its corresponding base-36 value, otherwise return a value >= 127. template - unsigned char + _GLIBCXX23_CONSTEXPR unsigned char __from_chars_alnum_to_val(unsigned char __c) { if _GLIBCXX17_CONSTEXPR (_DecOnly) return static_cast(__c - '0'); else { +#if __cpp_lib_constexpr_charconv + if (std::__is_constant_evaluated()) + return __table<_DecOnly>.__data[__c]; +#endif // This initializer is deliberately made dependent in order to work // around modules bug PR105322. static constexpr auto __table = (_DecOnly, __from_chars_alnum_to_val_table()); @@ -451,7 +464,7 @@ namespace __detail /// std::from_chars implementation for integers in a power-of-two base. /// If _DecOnly is true, then we may assume __base is at most 8. template - bool + _GLIBCXX23_CONSTEXPR bool __from_chars_pow2_base(const char*& __first, const char* __last, _Tp& __val, int __base) { @@ -508,7 +521,7 @@ namespace __detail /// std::from_chars implementation for integers in any base. /// If _DecOnly is true, then we may assume __base is at most 10. template - bool + constexpr bool __from_chars_alnum(const char*& __first, const char* __last, _Tp& __val, int __base) { @@ -548,7 +561,7 @@ namespace __detail /// std::from_chars for integral types. template - __detail::__integer_from_chars_result_type<_Tp> + _GLIBCXX23_CONSTEXPR __detail::__integer_from_chars_result_type<_Tp> from_chars(const char* __first, const char* __last, _Tp& __value, int __base = 10) { diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 397a4aa7b0a..bec9e7aa792 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -302,6 +302,7 @@ #if __cplusplus > 202002L // c++23 #define __cpp_lib_byteswap 202110L +#define __cpp_lib_constexpr_charconv 202202L #define __cpp_lib_constexpr_typeinfo 202106L #if __cpp_concepts >= 202002L # define __cpp_lib_expected 202202L diff --git a/libstdc++-v3/testsuite/20_util/from_chars/constexpr.cc b/libstdc++-v3/testsuite/20_util/from_chars/constexpr.cc new file mode 100644 index 00000000000..6e146947c1f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/from_chars/constexpr.cc @@ -0,0 +1,57 @@ +// { dg-options "-std=gnu++23" } +// { dg-do compile { target c++23 } } + +#include +#include + +constexpr bool +test() +{ + const char str[] = "-01234afz###"; + const char* end = str + sizeof(str); + + std::from_chars_result res; + int ival = 99; + unsigned uval = 99; + + res = std::from_chars(str, str+1, ival, 10); + VERIFY( res.ptr == str ); + VERIFY( res.ec == std::errc::invalid_argument ); + VERIFY( ival == 99 ); + res = std::from_chars(str, str+4, ival, 10); + VERIFY( res.ptr == str+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( ival == -12 ); + res = std::from_chars(str, end, ival, 10); + VERIFY( res.ptr == str+6 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( ival == -1234 ); + + res = std::from_chars(str, end, uval, 10); + VERIFY( res.ptr == str ); + VERIFY( res.ec == std::errc::invalid_argument ); + VERIFY( uval == 99 ); + res = std::from_chars(str+1, end, uval, 10); + VERIFY( res.ptr == str+6 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( uval == 1234 ); + + res = std::from_chars(str, end, ival, 3); + VERIFY( res.ptr == str+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( ival == -5 ); + + res = std::from_chars(str, end, ival, 16); + VERIFY( res.ptr == str+8 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( ival == -1193135 ); + + res = std::from_chars(str+1, end, uval, 36); + VERIFY( res.ptr == str+1+8 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( uval == 2302953695 ); + + return true; +} + +static_assert( test() ); diff --git a/libstdc++-v3/testsuite/20_util/to_chars/constexpr.cc b/libstdc++-v3/testsuite/20_util/to_chars/constexpr.cc new file mode 100644 index 00000000000..30c591659ee --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/to_chars/constexpr.cc @@ -0,0 +1,172 @@ +// { dg-options "-std=gnu++23" } +// { dg-do compile { target c++23 } } + +#include + +#ifndef __cpp_lib_constexpr_charconv +# error "Feature-test macro for constexpr charconv missing in " +#elif __cpp_lib_constexpr_charconv != 202202L +# error "Feature-test macro for constexpr charconv has wrong value in " +#endif + +#include + +constexpr bool +test_base10() +{ + std::to_chars_result res; + char buf[10] = "XXXXXXXXX"; + res = std::to_chars(buf, buf+3, 1234); + VERIFY( res.ptr == buf+3 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+4, -1234); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+4, 1234); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '1' ); + VERIFY( buf[1] == '2' ); + VERIFY( buf[2] == '3' ); + VERIFY( buf[3] == '4' ); + VERIFY( buf[4] == 'X' ); + res = std::to_chars(buf, buf+10, -567, 10); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '-' ); + VERIFY( buf[1] == '5' ); + VERIFY( buf[2] == '6' ); + VERIFY( buf[3] == '7' ); + VERIFY( buf[4] == 'X' ); + return true; +} + +static_assert( test_base10() ); + +constexpr bool +test_base16() +{ + std::to_chars_result res; + char buf[10] = "XXXXXXXXX"; + res = std::to_chars(buf, buf+3, 0x1234, 16); + VERIFY( res.ptr == buf+3 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+4, -0x1234, 16); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+4, 0x1234, 16); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '1' ); + VERIFY( buf[1] == '2' ); + VERIFY( buf[2] == '3' ); + VERIFY( buf[3] == '4' ); + VERIFY( buf[4] == 'X' ); + res = std::to_chars(buf, buf+10, -0x567, 16); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '-' ); + VERIFY( buf[1] == '5' ); + VERIFY( buf[2] == '6' ); + VERIFY( buf[3] == '7' ); + VERIFY( buf[5] == 'X' ); + return true; +} + +static_assert( test_base16() ); + +constexpr bool +test_base8() +{ + std::to_chars_result res; + char buf[10] = "XXXXXXXXX"; + res = std::to_chars(buf, buf+2, 01234, 8); + VERIFY( res.ptr == buf+2 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+3, -01234, 8); + VERIFY( res.ptr == buf+3 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+4, 01234, 8); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '1' ); + VERIFY( buf[1] == '2' ); + VERIFY( buf[2] == '3' ); + VERIFY( buf[3] == '4' ); + VERIFY( buf[4] == 'X' ); + res = std::to_chars(buf, buf+10, -0567, 8); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '-' ); + VERIFY( buf[1] == '5' ); + VERIFY( buf[2] == '6' ); + VERIFY( buf[3] == '7' ); + VERIFY( buf[4] == 'X' ); + return true; +} + +static_assert( test_base8() ); + +constexpr bool +test_base2() +{ + std::to_chars_result res; + char buf[10] = "XXXXXXXXX"; + res = std::to_chars(buf, buf+4, 0b10001, 2); + VERIFY( res.ptr == buf+4 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+5, -0b10001, 2); + VERIFY( res.ptr == buf+5 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+5, 0b10001, 2); + VERIFY( res.ptr == buf+5 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '1' ); + VERIFY( buf[1] == '0' ); + VERIFY( buf[2] == '0' ); + VERIFY( buf[3] == '0' ); + VERIFY( buf[4] == '1' ); + VERIFY( buf[5] == 'X' ); + res = std::to_chars(buf, buf+10, -0b11011, 2); + VERIFY( res.ptr == buf+6 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '-' ); + VERIFY( buf[1] == '1' ); + VERIFY( buf[2] == '1' ); + VERIFY( buf[3] == '0' ); + VERIFY( buf[4] == '1' ); + VERIFY( buf[5] == '1' ); + VERIFY( buf[6] == 'X' ); + return true; +} + +static_assert( test_base2() ); + +constexpr bool +test_base36() +{ + std::to_chars_result res; + char buf[10] = "XXXXXXXXX"; + res = std::to_chars(buf, buf+1, 1234, 36); + VERIFY( res.ptr == buf+1 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+2, -1234, 36); + VERIFY( res.ptr == buf+2 ); + VERIFY( res.ec == std::errc::value_too_large ); + res = std::to_chars(buf, buf+3, 1234, 36); + VERIFY( res.ptr == buf+2 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == 'y' ); + VERIFY( buf[1] == 'a' ); + VERIFY( buf[3] == 'X' ); + res = std::to_chars(buf, buf+10, -567, 36); + VERIFY( res.ptr == buf+3 ); + VERIFY( res.ec == std::errc{} ); + VERIFY( buf[0] == '-' ); + VERIFY( buf[1] == 'f' ); + VERIFY( buf[2] == 'r' ); + VERIFY( buf[4] == 'X' ); + return true; +} + +static_assert( test_base36() ); diff --git a/libstdc++-v3/testsuite/20_util/to_chars/version.cc b/libstdc++-v3/testsuite/20_util/to_chars/version.cc new file mode 100644 index 00000000000..af06e1bf054 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/to_chars/version.cc @@ -0,0 +1,16 @@ +// { dg-options "-std=gnu++23" } +// { dg-do preprocess { target c++23 } } + +#include + +#ifndef __cpp_lib_to_chars +# error "Feature-test macro for to_chars missing in " +#elif __cpp_lib_to_chars != 201611L +# error "Feature-test macro for to_chars has wrong value in " +#endif + +#ifndef __cpp_lib_constexpr_charconv +# error "Feature-test macro for constexpr charconv missing in " +#elif __cpp_lib_constexpr_charconv != 202202L +# error "Feature-test macro for constexpr charconv has wrong value in " +#endif