[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