[libc++][stringbuf] Test and document LWG2995. (#100879)
As mentioned in the LWG issue libc++ has already implemented the optimization. This adds tests and documents the implementation defined behaviour. Drive-by fixes an initialization.
This commit is contained in:
parent
4e89d1199c
commit
d5a6ec1d4d
@ -62,6 +62,14 @@ E.g.
|
||||
- ``std::hermite(unsigned n, T x)`` for ``n >= 128``
|
||||
|
||||
|
||||
`[stringbuf.cons] <http://eel.is/c++draft/stringbuf.cons>`_ Whether sequence pointers are initialized to null pointers
|
||||
----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Libc++ does not initialize the pointers to null pointers. It resizes the buffer
|
||||
to its capacity and uses that size. This means the SSO buffer of
|
||||
``std::string`` is used as initial output buffer.
|
||||
|
||||
|
||||
Listed in the index of implementation-defined behavior
|
||||
======================================================
|
||||
|
||||
|
@ -101,7 +101,7 @@
|
||||
"`2936 <https://wg21.link/LWG2936>`__","Path comparison is defined in terms of the generic format","San Diego","|Complete|",""
|
||||
"`2943 <https://wg21.link/LWG2943>`__","Problematic specification of the wide version of ``basic_filebuf::open``\ ","San Diego","|Nothing To Do|",""
|
||||
"`2960 <https://wg21.link/LWG2960>`__","[fund.ts.v3] ``nonesuch``\ is insufficiently useless","San Diego","|Complete|",""
|
||||
"`2995 <https://wg21.link/LWG2995>`__","``basic_stringbuf``\ default constructor forbids it from using SSO capacity","San Diego","",""
|
||||
"`2995 <https://wg21.link/LWG2995>`__","``basic_stringbuf``\ default constructor forbids it from using SSO capacity","San Diego","|Complete|","20.0"
|
||||
"`2996 <https://wg21.link/LWG2996>`__","Missing rvalue overloads for ``shared_ptr``\ operations","San Diego","|Complete|","17.0"
|
||||
"`3008 <https://wg21.link/LWG3008>`__","``make_shared``\ (sub)object destruction semantics are not specified","San Diego","|Complete|","16.0"
|
||||
"`3022 <https://wg21.link/LWG3022>`__","``is_convertible<derived*, base*>``\ may lead to ODR","San Diego","Resolved by `P1285R0 <https://wg21.link/P1285R0>`__",""
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
@ -354,9 +354,15 @@ private:
|
||||
|
||||
public:
|
||||
// [stringbuf.cons] constructors:
|
||||
_LIBCPP_HIDE_FROM_ABI basic_stringbuf() : __hm_(nullptr), __mode_(ios_base::in | ios_base::out) {}
|
||||
_LIBCPP_HIDE_FROM_ABI basic_stringbuf() : __hm_(nullptr), __mode_(ios_base::in | ios_base::out) {
|
||||
// it is implementation-defined whether we initialize eback() & friends to nullptr, and libc++ doesn't
|
||||
__init_buf_ptrs();
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI explicit basic_stringbuf(ios_base::openmode __wch) : __hm_(nullptr), __mode_(__wch) {}
|
||||
_LIBCPP_HIDE_FROM_ABI explicit basic_stringbuf(ios_base::openmode __wch) : __hm_(nullptr), __mode_(__wch) {
|
||||
// it is implementation-defined whether we initialize eback() & friends to nullptr, and libc++ doesn't
|
||||
__init_buf_ptrs();
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI explicit basic_stringbuf(const string_type& __s,
|
||||
ios_base::openmode __wch = ios_base::in | ios_base::out)
|
||||
@ -369,7 +375,9 @@ public:
|
||||
: basic_stringbuf(ios_base::in | ios_base::out, __a) {}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI basic_stringbuf(ios_base::openmode __wch, const allocator_type& __a)
|
||||
: __str_(__a), __hm_(nullptr), __mode_(__wch) {}
|
||||
: __str_(__a), __hm_(nullptr), __mode_(__wch) {
|
||||
__init_buf_ptrs();
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI explicit basic_stringbuf(string_type&& __s,
|
||||
ios_base::openmode __wch = ios_base::in | ios_base::out)
|
||||
|
@ -0,0 +1,169 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <sstream>
|
||||
|
||||
// How the constructors of basic_stringbuf initialize the buffer pointers is
|
||||
// not specified. For some constructors it's implementation defined whether the
|
||||
// pointers are set to nullptr. Libc++'s implementation directly uses the SSO
|
||||
// buffer of a std::string as the initial size. This test validates that
|
||||
// behaviour.
|
||||
//
|
||||
// This behaviour is allowed by LWG2995.
|
||||
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "min_allocator.h"
|
||||
|
||||
template <class CharT>
|
||||
struct test_buf : public std::basic_stringbuf<CharT> {
|
||||
typedef std::basic_streambuf<CharT> base;
|
||||
typedef typename base::char_type char_type;
|
||||
typedef typename base::int_type int_type;
|
||||
typedef typename base::traits_type traits_type;
|
||||
|
||||
char_type* pbase() const { return base::pbase(); }
|
||||
char_type* pptr() const { return base::pptr(); }
|
||||
char_type* epptr() const { return base::epptr(); }
|
||||
void gbump(int n) { base::gbump(n); }
|
||||
|
||||
virtual int_type overflow(int_type c = traits_type::eof()) { return base::overflow(c); }
|
||||
|
||||
test_buf() = default;
|
||||
explicit test_buf(std::ios_base::openmode which) : std::basic_stringbuf<CharT>(which) {}
|
||||
|
||||
explicit test_buf(const std::basic_string<CharT>& s) : std::basic_stringbuf<CharT>(s) {}
|
||||
#if TEST_STD_VER >= 20
|
||||
explicit test_buf(const std::allocator<CharT>& a) : std::basic_stringbuf<CharT>(a) {}
|
||||
test_buf(std::ios_base::openmode which, const std::allocator<CharT>& a) : std::basic_stringbuf<CharT>(which, a) {}
|
||||
explicit test_buf(std::basic_string<CharT>&& s)
|
||||
: std::basic_stringbuf<CharT>(std::forward<std::basic_string<CharT>>(s)) {}
|
||||
|
||||
test_buf(const std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>& s,
|
||||
const std::allocator<CharT>& a)
|
||||
: std::basic_stringbuf<CharT>(s, a) {}
|
||||
test_buf(const std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>& s,
|
||||
std::ios_base::openmode which,
|
||||
const std::allocator<CharT>& a)
|
||||
: std::basic_stringbuf<CharT>(s, which, a) {}
|
||||
test_buf(const std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>& s)
|
||||
: std::basic_stringbuf<CharT>(s) {}
|
||||
#endif // TEST_STD_VER >= 20
|
||||
|
||||
#if TEST_STD_VER >= 26
|
||||
test_buf(std::basic_string_view<CharT> s) : std::basic_stringbuf<CharT>(s) {}
|
||||
test_buf(std::basic_string_view<CharT> s, const std::allocator<CharT>& a) : std::basic_stringbuf<CharT>(s, a) {}
|
||||
test_buf(std::basic_string_view<CharT> s, std::ios_base::openmode which, const std::allocator<CharT>& a)
|
||||
: std::basic_stringbuf<CharT>(s, which, a) {}
|
||||
#endif // TEST_STD_VER >= 26
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
static void test() {
|
||||
std::size_t size = std::basic_string<CharT>().capacity(); // SSO buffer size.
|
||||
{
|
||||
test_buf<CharT> b;
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
{
|
||||
test_buf<CharT> b(std::ios_base::out);
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
{
|
||||
std::basic_string<CharT> s;
|
||||
s.reserve(1024);
|
||||
test_buf<CharT> b(s);
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size); // copy so uses size
|
||||
}
|
||||
#if TEST_STD_VER >= 20
|
||||
{
|
||||
test_buf<CharT> b = test_buf<CharT>(std::allocator<CharT>());
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
{
|
||||
test_buf<CharT> b = test_buf<CharT>(std::ios_base::out, std::allocator<CharT>());
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
{
|
||||
std::basic_string<CharT> s;
|
||||
s.reserve(1024);
|
||||
std::size_t capacity = s.capacity();
|
||||
test_buf<CharT> b = test_buf<CharT>(std::move(s));
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() >= b.pbase() + capacity); // move so uses s.capacity()
|
||||
}
|
||||
{
|
||||
std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>> s;
|
||||
s.reserve(1024);
|
||||
test_buf<CharT> b = test_buf<CharT>(s, std::allocator<CharT>());
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size); // copy so uses size
|
||||
}
|
||||
{
|
||||
std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>> s;
|
||||
s.reserve(1024);
|
||||
test_buf<CharT> b = test_buf<CharT>(s, std::ios_base::out, std::allocator<CharT>());
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size); // copy so uses size
|
||||
}
|
||||
{
|
||||
std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>> s;
|
||||
s.reserve(1024);
|
||||
test_buf<CharT> b = test_buf<CharT>(s);
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size); // copy so uses size
|
||||
}
|
||||
#endif // TEST_STD_VER >= 20
|
||||
#if TEST_STD_VER >= 26
|
||||
{
|
||||
std::basic_string_view<CharT> s;
|
||||
test_buf<CharT> b = test_buf<CharT>(s);
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
{
|
||||
std::basic_string_view<CharT> s;
|
||||
test_buf<CharT> b = test_buf<CharT>(s, std::allocator<CharT>());
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
{
|
||||
std::basic_string_view<CharT> s;
|
||||
test_buf<CharT> b = test_buf<CharT>(s, std::ios_base::out, std::allocator<CharT>());
|
||||
assert(b.pbase() != nullptr);
|
||||
assert(b.pptr() == b.pbase());
|
||||
assert(b.epptr() == b.pbase() + size);
|
||||
}
|
||||
#endif // TEST_STD_VER >= 26
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<char>();
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
@ -29,12 +29,18 @@ struct testbuf
|
||||
{
|
||||
void check()
|
||||
{
|
||||
assert(this->eback() == NULL);
|
||||
assert(this->gptr() == NULL);
|
||||
assert(this->egptr() == NULL);
|
||||
assert(this->pbase() == NULL);
|
||||
assert(this->pptr() == NULL);
|
||||
assert(this->epptr() == NULL);
|
||||
// LWG2995
|
||||
// It is implementation-defined whether the sequence pointers (eback(),
|
||||
// gptr(), egptr(), pbase(), pptr(), epptr()) are initialized to null
|
||||
// pointers.
|
||||
// This tests the libc++ specific implementation.
|
||||
LIBCPP_ASSERT(this->eback() != nullptr);
|
||||
LIBCPP_ASSERT(this->gptr() != nullptr);
|
||||
LIBCPP_ASSERT(this->egptr() != nullptr);
|
||||
LIBCPP_ASSERT(this->pbase() != nullptr);
|
||||
LIBCPP_ASSERT(this->pptr() != nullptr);
|
||||
LIBCPP_ASSERT(this->epptr() != nullptr);
|
||||
assert(this->str().empty());
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user