From patchwork Sat Nov 13 11:46:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1554680 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=HE6tM/Pl; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from 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 RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4Hrtx601KZz9sCD for ; Sat, 13 Nov 2021 22:47:36 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A89D73857C50 for ; Sat, 13 Nov 2021 11:47:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A89D73857C50 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1636804053; bh=YLyBgmrr80ltRmdURgwY3W11JBrFntZ15UtlH0ZYuew=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=HE6tM/Plge1Xzmwk+K+JvMjx1b20fwsJ9QhUV+kqJsCDU1h3xA0DYzI6QbHoPPoAb 02+c9O4DBBdlE9HiOYa4QejUE/sE7fg6DToRyA4JXZwetiZ5Sr7ealZbUCSo+j11Gl hg9hqPQQRAL6mLyUrY0r1IwqLeeJw+8o3kazkTAg= 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 85ABD3857C44 for ; Sat, 13 Nov 2021 11:47:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 85ABD3857C44 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-458-CzxxZMJtM96V3nhHdtAw3A-1; Sat, 13 Nov 2021 06:47:01 -0500 X-MC-Unique: CzxxZMJtM96V3nhHdtAw3A-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id EF8A218D6A25; Sat, 13 Nov 2021 11:47:00 +0000 (UTC) Received: from localhost (unknown [10.33.36.17]) by smtp.corp.redhat.com (Postfix) with ESMTP id 757EB5D6CF; Sat, 13 Nov 2021 11:47:00 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Implement std::spanstream for C++23 Date: Sat, 13 Nov 2021 11:46:59 +0000 Message-Id: <20211113114659.3408058-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_NUMSUBJECT, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" The tests are just the two small examples from the proposal, so more tests are definitely needed. They can wait for stage 3 though. Tested powerpc64le-linux, pushed to trunk. This implements the header, as proposed for C++23 by P0448R4. libstdc++-v3/ChangeLog: * include/Makefile.am: Add spanstream header. * include/Makefile.in: Regenerate. * include/precompiled/stdc++.h: Add spanstream header. * include/std/version (__cpp_lib_spanstream): Define. * include/std/spanstream: New file. * testsuite/27_io/spanstream/1.cc: New test. * testsuite/27_io/spanstream/version.cc: New test. --- libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/precompiled/stdc++.h | 6 +- libstdc++-v3/include/std/spanstream | 446 ++++++++++++++++++ libstdc++-v3/include/std/version | 3 + libstdc++-v3/testsuite/27_io/spanstream/1.cc | 53 +++ .../testsuite/27_io/spanstream/version.cc | 10 + 7 files changed, 519 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/std/spanstream create mode 100644 libstdc++-v3/testsuite/27_io/spanstream/1.cc create mode 100644 libstdc++-v3/testsuite/27_io/spanstream/version.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 0e43f147591..25a8d9c8a41 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -76,6 +76,7 @@ std_headers = \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/source_location \ ${std_srcdir}/span \ + ${std_srcdir}/spanstream \ ${std_srcdir}/sstream \ ${std_srcdir}/syncstream \ ${std_srcdir}/stack \ diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h index d2601d7859d..e1c10e612e8 100644 --- a/libstdc++-v3/include/precompiled/stdc++.h +++ b/libstdc++-v3/include/precompiled/stdc++.h @@ -133,7 +133,7 @@ #include #endif -#if __cplusplus > 201703L +#if __cplusplus >= 202002L #include #include #include @@ -151,3 +151,7 @@ #include #include #endif + +#if __cplusplus > 202002L +#include +#endif diff --git a/libstdc++-v3/include/std/spanstream b/libstdc++-v3/include/std/spanstream new file mode 100644 index 00000000000..240866ff26f --- /dev/null +++ b/libstdc++-v3/include/std/spanstream @@ -0,0 +1,446 @@ +// Streams based on std::span -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file spanstream + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SPANSTREAM +#define _GLIBCXX_SPANSTREAM 1 + +#pragma GCC system_header + +#if __cplusplus > 202002L +#include +#include +#include +#include +#include + +#if __cpp_lib_span +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +#define __cpp_lib_spanstream 202106L + +template> + class basic_spanbuf + : public basic_streambuf<_CharT, _Traits> + { + using __streambuf_type = basic_streambuf<_CharT, _Traits>; + + public: + using char_type = _CharT; + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using traits_type = _Traits; + + // [spanbuf.ctor], constructors + basic_spanbuf() : basic_spanbuf(ios_base::in | ios_base::out) + { } + + explicit + basic_spanbuf(ios_base::openmode __which) + : __streambuf_type(), _M_mode(__which) + { } + + explicit + basic_spanbuf(std::span<_CharT> __s, + ios_base::openmode __which = ios_base::in | ios_base::out) + : __streambuf_type(), _M_mode(__which) + { span(__s); } + + basic_spanbuf(const basic_spanbuf&) = delete; + + /// Move constructor. In this implementation `rhs` is left unchanged. + basic_spanbuf(basic_spanbuf&& __rhs) + : __streambuf_type(__rhs), _M_mode(__rhs._M_mode) + { span(__rhs._M_buf); } + + // [spanbuf.assign], assignment and swap + basic_spanbuf& operator=(const basic_spanbuf&) = delete; + + basic_spanbuf& + operator=(basic_spanbuf&& __rhs) + { + basic_spanbuf(std::move(__rhs))->swap(*this); + return *this; + } + + void + swap(basic_spanbuf& __rhs) + { + __streambuf_type::swap(__rhs); + std::swap(_M_mode, __rhs._M_mode); + std::swap(_M_buf, __rhs._M_buf); + } + + // [spanbuf.members], member functions + std::span<_CharT> + span() const noexcept + { + if (_M_mode & ios_base::out) + return {this->pbase(), this->pptr()}; + else + return _M_buf; + } + + void + span(std::span<_CharT> __s) noexcept + { + _M_buf = __s; + if (_M_mode & ios_base::out) + { + this->setp(__s.data(), __s.data() + __s.size()); + if (_M_mode & ios_base::ate) + this->pbump(__s.size()); + } + if (_M_mode & ios_base::in) + this->setg(__s.data(), __s.data(), __s.data() + __s.size()); + } + + protected: + // [spanbuf.virtuals], overridden virtual functions + basic_streambuf<_CharT, _Traits>* + setbuf(_CharT* __s, streamsize __n) override + { + span({__s, __n}); + return this; + } + + pos_type + seekoff(off_type __off, ios_base::seekdir __way, + ios_base::openmode __which = ios_base::in | ios_base::out) override + { + pos_type __ret = pos_type(off_type(-1)); + + if (__way == ios_base::beg) + { + if (0 <= __off && __off <= _M_buf.size()) + { + if (__which & ios_base::in) + this->setg(this->eback(), this->eback() + __off, this->egptr()); + + if (__which & ios_base::out) + { + this->setp(this->pbase(), this->epptr()); + this->pbump(__off); + } + + __ret = pos_type(__off); + } + } + else + { + off_type __base; + __which &= (ios_base::in|ios_base::out); + + if (__which == ios_base::out) + __base = this->pptr() - this->pbase(); + else if (__way == ios_base::cur) + { + if (__which == ios_base::in) + __base = this->gptr() - this->eback(); + else + return __ret; + } + else if (__way == ios_base::end) + __base = _M_buf.size(); + + if (__builtin_add_overflow(__base, __off, &__off)) + return __ret; + + if (__off < 0 || __off > _M_buf.size()) + return __ret; + + if (__which & ios_base::in) + this->setg(this->eback(), this->eback() + __off, this->egptr()); + + if (__which & ios_base::out) + { + this->setp(this->pbase(), this->epptr()); + this->pbump(__off); + } + + __ret = pos_type(__off); + + } + return __ret; + } + + pos_type + seekpos(pos_type __sp, + ios_base::openmode __which = ios_base::in | ios_base::out) override + { return seekoff(off_type(__sp), ios_base::beg, __which); } + + private: + + ios_base::openmode _M_mode; + std::span<_CharT> _M_buf; + }; + +template + inline void + swap(basic_spanbuf<_CharT, _Traits>& __x, + basic_spanbuf<_CharT, _Traits>& __y) + { __x.swap(__y); } + +using spanbuf = basic_spanbuf; +using wspanbuf = basic_spanbuf; + +template> + class basic_ispanstream + : public basic_istream<_CharT, _Traits> + { + using __istream_type = basic_istream<_CharT, _Traits>; + + public: + using char_type = _CharT; + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using traits_type = _Traits; + + // [ispanstream.ctor], constructors + explicit + basic_ispanstream(std::span<_CharT> __s, + ios_base::openmode __which = ios_base::in) + : __istream_type(std::__addressof(_M_sb)), + _M_sb(__s, __which | ios_base::in) + { } + + basic_ispanstream(const basic_ispanstream&) = delete; + + basic_ispanstream(basic_ispanstream&& __rhs) + : __istream_type(std::move(__rhs)), _M_sb(std::move(__rhs._M_sb)) + { + __istream_type::set_rdbuf(std::addressof(_M_sb)); + } + + template + requires ranges::borrowed_range<_Ros> + && (!convertible_to<_Ros, std::span<_CharT>>) + && convertible_to<_Ros, std::span> + explicit + basic_ispanstream(_Ros&& __s) + : __istream_type(std::__addressof(_M_sb)), + _M_sb(ios_base::in) + { + std::span __sp(std::forward<_Ros>(__s)); + _M_sb.span({const_cast<_CharT*>(__sp.data()), __sp.size()}); + } + + // [ispanstream.assign], assignment and swap + basic_ispanstream& operator=(const basic_ispanstream&) = delete; + basic_ispanstream& operator=(basic_ispanstream&& __rhs) = default; + + void + swap(basic_ispanstream& __rhs) + { + __istream_type::swap(__rhs); + _M_sb.swap(__rhs._M_sb); + } + + // [ispanstream.members], member functions + basic_spanbuf<_CharT, _Traits>* + rdbuf() const noexcept + { + return const_cast*>(std::__addressof(_M_sb)); + } + + std::span + span() const noexcept + { return _M_sb.span(); } + + void + span(std::span<_CharT> __s) noexcept + { return _M_sb.span(__s); } + + template + requires ranges::borrowed_range<_Ros> + && (!convertible_to<_Ros, std::span<_CharT>>) + && convertible_to<_Ros, std::span> + void + span(_Ros&& __s) noexcept + { + std::span __sp(std::forward<_Ros>(__s)); + _M_sb.span({const_cast<_CharT*>(__sp.data()), __sp.size()}); + } + + private: + basic_spanbuf<_CharT, _Traits> _M_sb; + }; + +template + inline void + swap(basic_ispanstream<_CharT, _Traits>& __x, + basic_ispanstream<_CharT, _Traits>& __y) + { __x.swap(__y); } + +using ispanstream = basic_ispanstream; +using wispanstream = basic_ispanstream; + +template> + class basic_ospanstream + : public basic_ostream<_CharT, _Traits> + { + using __ostream_type = basic_ostream<_CharT, _Traits>; + + public: + using char_type = _CharT; + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using traits_type = _Traits; + + // [ospanstream.ctor], constructors + explicit + basic_ospanstream(std::span<_CharT> __s, + ios_base::openmode __which = ios_base::out) + : __ostream_type(std::__addressof(_M_sb)), + _M_sb(__s, __which | ios_base::in) + { } + + basic_ospanstream(const basic_ospanstream&) = delete; + + basic_ospanstream(basic_ospanstream&& __rhs) + : __ostream_type(std::move(__rhs)), _M_sb(std::move(__rhs._M_sb)) + { + __ostream_type::set_rdbuf(std::addressof(_M_sb)); + } + + // [ospanstream.assign], assignment and swap + basic_ospanstream& operator=(const basic_ospanstream&) = delete; + basic_ospanstream& operator=(basic_ospanstream&& __rhs) = default; + + void + swap(basic_ospanstream& __rhs) + { + __ostream_type::swap(__rhs); + _M_sb.swap(__rhs._M_sb); + } + + // [ospanstream.members], member functions + basic_spanbuf<_CharT, _Traits>* + rdbuf() const noexcept + { + return const_cast*>(std::__addressof(_M_sb)); + } + + std::span<_CharT> + span() const noexcept + { return _M_sb.span(); } + + void + span(std::span<_CharT> __s) noexcept + { return _M_sb.span(__s); } + + private: + basic_spanbuf<_CharT, _Traits> _M_sb; + }; + +template + inline void + swap(basic_ospanstream<_CharT, _Traits>& __x, + basic_ospanstream<_CharT, _Traits>& __y) + { __x.swap(__y); } + +using ospanstream = basic_ospanstream; +using wospanstream = basic_ospanstream; + +template> + class basic_spanstream + : public basic_iostream<_CharT, _Traits> + { + using __iostream_type = basic_iostream<_CharT, _Traits>; + + public: + using char_type = _CharT; + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using traits_type = _Traits; + + // [spanstream.ctor], constructors + explicit + basic_spanstream(std::span<_CharT> __s, + ios_base::openmode __which = ios_base::out | ios_base::in) + : __iostream_type(std::__addressof(_M_sb)), + _M_sb(__s, __which) + { } + + basic_spanstream(const basic_spanstream&) = delete; + + basic_spanstream(basic_spanstream&& __rhs) + : __iostream_type(std::move(__rhs)), _M_sb(std::move(__rhs._M_sb)) + { + __iostream_type::set_rdbuf(std::addressof(_M_sb)); + } + + // [spanstream.assign], assignment and swap + basic_spanstream& operator=(const basic_spanstream&) = delete; + basic_spanstream& operator=(basic_spanstream&& __rhs) = default; + + void + swap(basic_spanstream& __rhs) + { + __iostream_type::swap(__rhs); + _M_sb.swap(__rhs._M_sb); + } + + // [spanstream.members], members + basic_spanbuf<_CharT, _Traits>* + rdbuf() const noexcept + { + return const_cast*>(std::__addressof(_M_sb)); + } + + std::span<_CharT> + span() const noexcept + { return _M_sb.span(); } + + void + span(std::span<_CharT> __s) noexcept + { return _M_sb.span(__s); } + + private: + basic_spanbuf<_CharT, _Traits> _M_sb; + }; + +template + inline void + swap(basic_spanstream<_CharT, _Traits>& __x, + basic_spanstream<_CharT, _Traits>& __y) + { __x.swap(__y); } + +using spanstream = basic_spanstream; +using wspanstream = basic_spanstream; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // __cpp_lib_span +#endif // C++23 +#endif // _GLIBCXX_SPANSTREAM diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 0a7b28abee6..0930de82efa 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -296,6 +296,9 @@ # define __cpp_lib_monadic_optional 202110L #endif #define __cpp_lib_move_only_function 202110L +#if __cpp_lib_span +# define __cpp_lib_spanstream 202106L +#endif #define __cpp_lib_string_contains 202011L #if _GLIBCXX_USE_CXX11_ABI // Only supported with cxx11-abi # define __cpp_lib_string_resize_and_overwrite 202110L diff --git a/libstdc++-v3/testsuite/27_io/spanstream/1.cc b/libstdc++-v3/testsuite/27_io/spanstream/1.cc new file mode 100644 index 00000000000..b66ee60ec92 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/spanstream/1.cc @@ -0,0 +1,53 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include + +#ifndef __cpp_lib_spanstream +# error "Feature-test macro for spanstream missing in " +#elif __cpp_lib_spanstream != 202106L +# error "Feature-test macro for spanstream has wrong value in " +#endif + +#include + +using std::ispanstream; +using std::ospanstream; +using std::span; + +void +test_input() +{ + // reading input from a fixed pre-arranged character buffer + char input[] = "10 20 30"; + ispanstream is{span{input}}; + int i; + is >> i; + VERIFY(10 == i); + is >> i; + VERIFY(20 == i); + is >> i; + VERIFY(30 == i); + is >>i; + VERIFY(!is); +} + +void +test_output() +{ + // writing to a fixed pre-arranged character buffer + char output[30]{}; // zero-initialize array + ospanstream os{span{output}}; + os << 10 << 20 << 30; + auto const sp = os.span(); + VERIFY(6 == sp.size()); + VERIFY("102030" == std::string(sp.data(), sp.size())); + VERIFY(static_cast(output) == sp.data()); // no copying of underlying data! + VERIFY("102030" == std::string(output)); // initialization guaranteed NUL termination +} + +int main() +{ + test_input(); + test_output(); +} diff --git a/libstdc++-v3/testsuite/27_io/spanstream/version.cc b/libstdc++-v3/testsuite/27_io/spanstream/version.cc new file mode 100644 index 00000000000..62617550493 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/spanstream/version.cc @@ -0,0 +1,10 @@ +// { dg-options "-std=gnu++23" } +// { dg-do compile { target c++23 } } + +#include + +#ifndef __cpp_lib_spanstream +# error "Feature-test macro for spanstream missing in " +#elif __cpp_lib_spanstream != 202106L +# error "Feature-test macro for spanstream has wrong value in " +#endif