[libcxx-commits] [libcxx] [libc++] Implement p0753r2 Manipulators for C++ Synchronized Buffered Ostream (PR #97955)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 9 10:24:07 PDT 2024


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/97955

>From 3dc6024c30fcf3ced58abd6cd016b01fcf454a47 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Sun, 7 Jul 2024 15:56:49 +0100
Subject: [PATCH] [libc++] Implement p0753r2 Manipulators for C++ Synchronized
 Buffered Ostream

---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__ostream/syncbuf_base.h       | 92 +++++++++++++++++++
 libcxx/include/module.modulemap               |  4 +
 libcxx/include/ostream                        | 10 ++
 libcxx/include/syncstream                     | 20 ++--
 .../syncstream/manip/emit_on_flush.pass.cpp   | 45 +++++++++
 .../syncstream/manip/flush_emit.pass.cpp      | 47 ++++++++++
 .../syncstream/manip/noemit_on_flush.pass.cpp | 47 ++++++++++
 8 files changed, 258 insertions(+), 8 deletions(-)
 create mode 100644 libcxx/include/__ostream/syncbuf_base.h
 create mode 100644 libcxx/test/std/input.output/syncstream/manip/emit_on_flush.pass.cpp
 create mode 100644 libcxx/test/std/input.output/syncstream/manip/flush_emit.pass.cpp
 create mode 100644 libcxx/test/std/input.output/syncstream/manip/noemit_on_flush.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8d0ffd6ed725b..9cc96730923b8 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -575,6 +575,7 @@ set(files
   __numeric/transform_reduce.h
   __ostream/basic_ostream.h
   __ostream/print.h
+  __ostream/syncbuf_base.h
   __pstl/backend.h
   __pstl/backend_fwd.h
   __pstl/backends/default.h
diff --git a/libcxx/include/__ostream/syncbuf_base.h b/libcxx/include/__ostream/syncbuf_base.h
new file mode 100644
index 0000000000000..fb49f94bc5704
--- /dev/null
+++ b/libcxx/include/__ostream/syncbuf_base.h
@@ -0,0 +1,92 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___OSTREAM_SYNCBUF_BASE_H
+#define _LIBCPP___OSTREAM_SYNCBUF_BASE_H
+
+#include <__config>
+#include <__ostream/basic_ostream.h>
+#include <streambuf>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
+
+template <class _CharT, class _Traits>
+class _LIBCPP_TEMPLATE_VIS __syncbuf_base : public basic_streambuf<_CharT, _Traits> {
+public:
+
+protected:
+  _LIBCPP_HIDE_FROM_ABI explicit __syncbuf_base(bool __b = false) : __emit_on_sync_(__b) {}
+
+private:
+  bool __emit_on_sync_{false};
+
+  virtual bool __emit() = 0;
+
+  template <class, class, class>
+  friend class basic_syncbuf;
+
+  friend struct __syncbuf_base_access;
+};
+
+struct __syncbuf_base_access {
+  template <class _CharT, class _Traits>
+  _LIBCPP_HIDE_FROM_ABI static void __set_emit_on_sync(__syncbuf_base<_CharT, _Traits>* __buf, bool __b) {
+    __buf->__emit_on_sync_ = __b;
+  }
+
+  template <class _CharT, class _Traits>
+  _LIBCPP_HIDE_FROM_ABI static bool __emit(__syncbuf_base<_CharT, _Traits>* __buf) {
+    return __buf->__emit();
+  }
+};
+
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>& emit_on_flush(basic_ostream<_CharT, _Traits>& __os) {
+  if (auto* __buf = dynamic_cast<__syncbuf_base<_CharT, _Traits>*>(__os.rdbuf())) {
+    __syncbuf_base_access::__set_emit_on_sync(__buf, true);
+  }
+  return __os;
+}
+
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>& noemit_on_flush(basic_ostream<_CharT, _Traits>& __os) {
+  if (auto* __buf = dynamic_cast<__syncbuf_base<_CharT, _Traits>*>(__os.rdbuf())) {
+    __syncbuf_base_access::__set_emit_on_sync(__buf, false);
+  }
+  return __os;
+}
+
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>& flush_emit(basic_ostream<_CharT, _Traits>& __os) {
+  __os.flush();
+  if (auto* __buf = dynamic_cast<__syncbuf_base<_CharT, _Traits>*>(__os.rdbuf())) {
+    // The standard specifies that:
+    // After constructing a sentry object, calls buf->emit().
+    // If that call returns false, calls os.setstate(ios_base::badbit).
+    //
+    // syncstream::emit already constructs a sentry
+    bool __emit_result = __syncbuf_base_access::__emit(__buf);
+    if (!__emit_result) {
+      __os.setstate(ios_base::badbit);
+    }
+  }
+  return __os;
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___OSTREAM_SYNCBUF_BASE_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 9ffccf66ff094..4b9921833c511 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1631,6 +1631,10 @@ module std_private_ostream_print         [system] {
   export std_print
 }
 
+module std_private_ostream_syncbuf_base  [system] {
+  header "__ostream/syncbuf_base.h"
+}
+
 module std_private_random_bernoulli_distribution          [system] { header "__random/bernoulli_distribution.h" }
 module std_private_random_binomial_distribution           [system] { header "__random/binomial_distribution.h" }
 module std_private_random_cauchy_distribution             [system] { header "__random/cauchy_distribution.h" }
diff --git a/libcxx/include/ostream b/libcxx/include/ostream
index f75110e7d73f7..afb1a8b2f39c0 100644
--- a/libcxx/include/ostream
+++ b/libcxx/include/ostream
@@ -126,6 +126,15 @@ template <class charT, class traits>
 template <class charT, class traits>
   basic_ostream<charT,traits>& flush(basic_ostream<charT,traits>& os);
 
+template<class charT, class traits>
+  basic_ostream<charT, traits>& emit_on_flush(basic_ostream<charT, traits>& os);                       // since C++20
+  
+template<class charT, class traits>
+  basic_ostream<charT, traits>& noemit_on_flush(basic_ostream<charT, traits>& os);                     // since C++20
+  
+template<class charT, class traits>
+  basic_ostream<charT, traits>& flush_emit(basic_ostream<charT, traits>& os);                          // since C++20
+
 // rvalue stream insertion
 template <class Stream, class T>
   Stream&& operator<<(Stream&& os, const T& x);
@@ -175,6 +184,7 @@ void vprint_nonunicode(ostream& os, string_view fmt, format_args args);
 #include <__config>
 #include <__ostream/basic_ostream.h>
 #include <__ostream/print.h>
+#include <__ostream/syncbuf_base.h>
 #include <version>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/syncstream b/libcxx/include/syncstream
index e6f35b6f428ed..243d6a56a5dc9 100644
--- a/libcxx/include/syncstream
+++ b/libcxx/include/syncstream
@@ -116,6 +116,7 @@ namespace std {
 */
 
 #include <__config>
+#include <__ostream/syncbuf_base.h>
 #include <__utility/move.h>
 #include <ios>
 #include <iosfwd> // required for declaration of default arguments
@@ -240,7 +241,7 @@ private:
 // Therefore the allocator used in the constructor is passed to the
 // basic_string. The class does not keep a copy of this allocator.
 template <class _CharT, class _Traits, class _Allocator>
-class _LIBCPP_TEMPLATE_VIS basic_syncbuf : public basic_streambuf<_CharT, _Traits> {
+class _LIBCPP_TEMPLATE_VIS basic_syncbuf : public __syncbuf_base<_CharT, _Traits> {
 public:
   using char_type      = _CharT;
   using traits_type    = _Traits;
@@ -262,7 +263,9 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other)
-      : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) {
+      : __syncbuf_base<_CharT, _Traits>(__other.__emit_on_sync_),
+        __wrapped_(__other.get_wrapped()),
+        __str_(std::move(__other.__str_)) {
     __move_common(__other);
   }
 
@@ -286,9 +289,9 @@ public:
     emit();
     __dec_reference();
 
-    __wrapped_      = __other.get_wrapped();
-    __str_          = std::move(__other.__str_);
-    __emit_on_sync_ = __other.__emit_on_sync_;
+    __wrapped_            = __other.get_wrapped();
+    __str_                = std::move(__other.__str_);
+    this->__emit_on_sync_ = __other.__emit_on_sync_;
 
     __move_common(__other);
 
@@ -313,14 +316,14 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); }
 
-  _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; }
+  _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { this->__emit_on_sync_ = __b; }
 
 protected:
   // [syncstream.syncbuf.virtuals], overridden virtual functions
 
   _LIBCPP_HIDE_FROM_ABI_VIRTUAL
   int sync() override {
-    if (__emit_on_sync_ && !emit(true))
+    if (this->__emit_on_sync_ && !emit(true))
       return -1;
     return 0;
   }
@@ -361,7 +364,6 @@ private:
   // the now deprecated get_temporary_buffer
 
   basic_string<_CharT, _Traits, _Allocator> __str_;
-  bool __emit_on_sync_{false};
 
   _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) {
     if (!__wrapped_)
@@ -389,6 +391,8 @@ private:
     return __result;
   }
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL bool __emit() override { return emit(); }
+
   _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) {
     // Adjust the put area pointers to our buffer.
     char_type* __p = static_cast<char_type*>(__str_.data());
diff --git a/libcxx/test/std/input.output/syncstream/manip/emit_on_flush.pass.cpp b/libcxx/test/std/input.output/syncstream/manip/emit_on_flush.pass.cpp
new file mode 100644
index 0000000000000..f505aa9e77537
--- /dev/null
+++ b/libcxx/test/std/input.output/syncstream/manip/emit_on_flush.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-localization
+// UNSUPPORTED: libcpp-has-no-experimental-syncstream
+
+// template<class charT, class traits>
+// basic_ostream<charT, traits>& emit_on_flush(basic_ostream<charT, traits>& os);
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <syncstream>
+
+template <class CharT>
+void test_emit_on_flush() {
+  {
+    // non sync stream: nothing happens
+    std::basic_ostringstream<CharT> os;
+    std::emit_on_flush(os);
+  }
+  {
+    std::basic_stringbuf<CharT> buf;
+    std::basic_osyncstream<CharT> ss(&buf);
+    std::emit_on_flush(ss);
+    ss << 5;
+    ss.flush();
+    assert(!buf.str().empty());
+  }
+}
+
+int main(int, char**) {
+  test_emit_on_flush<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_emit_on_flush<wchar_t>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/syncstream/manip/flush_emit.pass.cpp b/libcxx/test/std/input.output/syncstream/manip/flush_emit.pass.cpp
new file mode 100644
index 0000000000000..d01f1b53c0606
--- /dev/null
+++ b/libcxx/test/std/input.output/syncstream/manip/flush_emit.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-localization
+// UNSUPPORTED: libcpp-has-no-experimental-syncstream
+
+// template<class charT, class traits>
+// basic_ostream<charT, traits>& flush_emit(basic_ostream<charT, traits>& os);
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <syncstream>
+
+template <class CharT>
+void test_flush_emit() {
+  {
+    // non sync stream: just flush
+    std::basic_ostringstream<CharT> os;
+    os << 5;
+    std::flush_emit(os);
+    assert(!os.rdbuf()->str().empty());
+  }
+  {
+    std::basic_stringbuf<CharT> buf;
+    std::basic_osyncstream<CharT> ss(&buf);
+    ss << 5;
+    assert(buf.str().empty());
+    std::flush_emit(ss);
+    assert(!buf.str().empty());
+  }
+}
+
+int main(int, char**) {
+  test_flush_emit<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_flush_emit<wchar_t>();
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/input.output/syncstream/manip/noemit_on_flush.pass.cpp b/libcxx/test/std/input.output/syncstream/manip/noemit_on_flush.pass.cpp
new file mode 100644
index 0000000000000..212788cda4b89
--- /dev/null
+++ b/libcxx/test/std/input.output/syncstream/manip/noemit_on_flush.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-localization
+// UNSUPPORTED: libcpp-has-no-experimental-syncstream
+
+// template<class charT, class traits>
+// basic_ostream<charT, traits>& noemit_on_flush(basic_ostream<charT, traits>& os);
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <syncstream>
+
+template <class CharT>
+void test_noemit_on_flush() {
+  {
+    // non sync stream: nothing happens
+    std::basic_ostringstream<CharT> os;
+    std::noemit_on_flush(os);
+  }
+  {
+    std::basic_stringbuf<CharT> buf;
+    std::basic_osyncstream<CharT> ss(&buf);
+    std::noemit_on_flush(ss);
+    ss << 5;
+    ss.flush();
+    assert(buf.str().empty());
+    ss.emit();
+    assert(!buf.str().empty());
+  }
+}
+
+int main(int, char**) {
+  test_noemit_on_flush<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_noemit_on_flush<wchar_t>();
+#endif
+
+  return 0;
+}



More information about the libcxx-commits mailing list