diff mbox series

[committed] libstdc++: Implement std::spanstream for C++23

Message ID 20211113114659.3408058-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Implement std::spanstream for C++23 | expand

Commit Message

Jonathan Wakely Nov. 13, 2021, 11:46 a.m. UTC
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 <spanstream> 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 mbox series

Patch

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 <variant>
 #endif
 
-#if __cplusplus > 201703L
+#if __cplusplus >= 202002L
 #include <barrier>
 #include <bit>
 #include <compare>
@@ -151,3 +151,7 @@ 
 #include <syncstream>
 #include <version>
 #endif
+
+#if __cplusplus > 202002L
+#include <spanstream>
+#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
+// <http://www.gnu.org/licenses/>.
+
+/** @file spanstream
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_SPANSTREAM
+#define _GLIBCXX_SPANSTREAM 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 202002L
+#include <span>
+#include <streambuf>
+#include <istream>
+#include <ostream>
+#include <bits/ranges_base.h>
+
+#if __cpp_lib_span
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#define __cpp_lib_spanstream 202106L
+
+template<typename _CharT, typename _Traits = char_traits<_CharT>>
+  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<typename _CharT, typename _Traits>
+  inline void
+  swap(basic_spanbuf<_CharT, _Traits>& __x,
+       basic_spanbuf<_CharT, _Traits>& __y)
+  { __x.swap(__y); }
+
+using spanbuf = basic_spanbuf<char>;
+using wspanbuf = basic_spanbuf<wchar_t>;
+
+template<typename _CharT, typename _Traits = char_traits<_CharT>>
+  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<typename _Ros>
+      requires ranges::borrowed_range<_Ros>
+	&& (!convertible_to<_Ros, std::span<_CharT>>)
+	&& convertible_to<_Ros, std::span<const _CharT>>
+      explicit
+      basic_ispanstream(_Ros&& __s)
+      : __istream_type(std::__addressof(_M_sb)),
+	_M_sb(ios_base::in)
+      {
+	std::span<const _CharT> __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<basic_spanbuf<_CharT, _Traits>*>(std::__addressof(_M_sb));
+    }
+
+    std::span<const _CharT>
+    span() const noexcept
+    { return _M_sb.span(); }
+
+    void
+    span(std::span<_CharT> __s) noexcept
+    { return _M_sb.span(__s); }
+
+    template<typename _Ros>
+      requires ranges::borrowed_range<_Ros>
+	&& (!convertible_to<_Ros, std::span<_CharT>>)
+	&& convertible_to<_Ros, std::span<const _CharT>>
+      void
+      span(_Ros&& __s) noexcept
+      {
+	std::span<const _CharT> __sp(std::forward<_Ros>(__s));
+	_M_sb.span({const_cast<_CharT*>(__sp.data()), __sp.size()});
+      }
+
+  private:
+    basic_spanbuf<_CharT, _Traits> _M_sb;
+  };
+
+template<typename _CharT, typename _Traits>
+  inline void
+  swap(basic_ispanstream<_CharT, _Traits>& __x,
+       basic_ispanstream<_CharT, _Traits>& __y)
+  { __x.swap(__y); }
+
+using ispanstream = basic_ispanstream<char>;
+using wispanstream = basic_ispanstream<wchar_t>;
+
+template<typename _CharT, typename _Traits = char_traits<_CharT>>
+  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<basic_spanbuf<_CharT, _Traits>*>(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<typename _CharT, typename _Traits>
+  inline void
+  swap(basic_ospanstream<_CharT, _Traits>& __x,
+       basic_ospanstream<_CharT, _Traits>& __y)
+  { __x.swap(__y); }
+
+using ospanstream = basic_ospanstream<char>;
+using wospanstream = basic_ospanstream<wchar_t>;
+
+template<typename _CharT, typename _Traits = char_traits<_CharT>>
+  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<basic_spanbuf<_CharT, _Traits>*>(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<typename _CharT, typename _Traits>
+  inline void
+  swap(basic_spanstream<_CharT, _Traits>& __x,
+       basic_spanstream<_CharT, _Traits>& __y)
+  { __x.swap(__y); }
+
+using spanstream = basic_spanstream<char>;
+using wspanstream = basic_spanstream<wchar_t>;
+
+_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 <spanstream>
+
+#ifndef __cpp_lib_spanstream
+# error "Feature-test macro for spanstream missing in <spanstream>"
+#elif __cpp_lib_spanstream != 202106L
+# error "Feature-test macro for spanstream has wrong value in <spanstream>"
+#endif
+
+#include <testsuite_hooks.h>
+
+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<char>{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<char>{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<void*>(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 <version>
+
+#ifndef __cpp_lib_spanstream
+# error "Feature-test macro for spanstream missing in <version>"
+#elif __cpp_lib_spanstream != 202106L
+# error "Feature-test macro for spanstream has wrong value in <version>"
+#endif