[libcxx-commits] [libcxx] d5a6ec1 - [libc++][stringbuf] Test and document LWG2995. (#100879)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Aug 1 08:00:17 PDT 2024


Author: Mark de Wever
Date: 2024-08-01T11:00:14-04:00
New Revision: d5a6ec1d4dd9a7593c68abfa49b75059ba98c91b

URL: https://github.com/llvm/llvm-project/commit/d5a6ec1d4dd9a7593c68abfa49b75059ba98c91b
DIFF: https://github.com/llvm/llvm-project/commit/d5a6ec1d4dd9a7593c68abfa49b75059ba98c91b.diff

LOG: [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.

Added: 
    libcxx/test/libcxx/input.output/string.streams/stringbuf/const_sso_buffer.pass.cpp

Modified: 
    libcxx/docs/ImplementationDefinedBehavior.rst
    libcxx/docs/Status/Cxx20Issues.csv
    libcxx/include/sstream
    libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/default.pass.cpp

Removed: 
    


################################################################################
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..272d8861d59c1 100644
--- a/libcxx/include/sstream
+++ b/libcxx/include/sstream
@@ -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)

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;
+}

diff  --git a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/default.pass.cpp b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/default.pass.cpp
index 6451d44e91b5c..d131f5c9a6fa7 100644
--- a/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/default.pass.cpp
+++ b/libcxx/test/std/input.output/string.streams/stringbuf/stringbuf.cons/default.pass.cpp
@@ -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());
     }
 };
 


        


More information about the libcxx-commits mailing list