libstdc++: Implement std::spanstream for C++23
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.
This commit is contained in:
parent
ecdf414bd8
commit
a30a2e43e4
7 changed files with 519 additions and 1 deletions
|
@ -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 \
|
||||
|
|
|
@ -426,6 +426,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 \
|
||||
|
|
|
@ -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
|
||||
|
|
446
libstdc++-v3/include/std/spanstream
Normal file
446
libstdc++-v3/include/std/spanstream
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
53
libstdc++-v3/testsuite/27_io/spanstream/1.cc
Normal file
53
libstdc++-v3/testsuite/27_io/spanstream/1.cc
Normal file
|
@ -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();
|
||||
}
|
10
libstdc++-v3/testsuite/27_io/spanstream/version.cc
Normal file
10
libstdc++-v3/testsuite/27_io/spanstream/version.cc
Normal file
|
@ -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
|
Loading…
Add table
Reference in a new issue