[libcxx-commits] [libcxx] [libc++][stringbuf] Test and document LWG2995. (PR #100879)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jul 27 09:24:12 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Mark de Wever (mordante)
<details>
<summary>Changes</summary>
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.
Implements
- LWG2995 basic_stringbuf default constructor forbids it from using SSO capacity
---
Full diff: https://github.com/llvm/llvm-project/pull/100879.diff
4 Files Affected:
- (modified) libcxx/docs/ImplementationDefinedBehavior.rst (+8)
- (modified) libcxx/docs/Status/Cxx20Issues.csv (+1-1)
- (modified) libcxx/include/sstream (+9-3)
- (added) libcxx/test/libcxx/input.output/string.streams/stringbuf/const_sso_buffer.pass.cpp (+169)
``````````diff
diff --git a/libcxx/docs/ImplementationDefinedBehavior.rst b/libcxx/docs/ImplementationDefinedBehavior.rst
index f0ef733fc2c55..1f95de77db0eb 100644
--- a/libcxx/docs/ImplementationDefinedBehavior.rst
+++ b/libcxx/docs/ImplementationDefinedBehavior.rst
@@ -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
======================================================
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 97ecf5e8e05a0..1b76b0c5acfff 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -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>`__",""
diff --git a/libcxx/include/sstream b/libcxx/include/sstream
index 9ba43ffeb850f..ba21492195bb8 100644
--- a/libcxx/include/sstream
+++ b/libcxx/include/sstream
@@ -354,9 +354,13 @@ 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) {
+ __init_buf_ptrs(); // this call is implementation defined.
+ }
- _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) {
+ __init_buf_ptrs(); // this call is implementation defined.
+ }
_LIBCPP_HIDE_FROM_ABI explicit basic_stringbuf(const string_type& __s,
ios_base::openmode __wch = ios_base::in | ios_base::out)
@@ -369,7 +373,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)
diff --git a/libcxx/test/libcxx/input.output/string.streams/stringbuf/const_sso_buffer.pass.cpp b/libcxx/test/libcxx/input.output/string.streams/stringbuf/const_sso_buffer.pass.cpp
new file mode 100644
index 0000000000000..d6caa3389b8fa
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/string.streams/stringbuf/const_sso_buffer.pass.cpp
@@ -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;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/100879
More information about the libcxx-commits
mailing list