| 1 | // -*- C++ -*- |
| 2 | //===----------------------------------------------------------------------===// |
| 3 | // |
| 4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 5 | // See https://llvm.org/LICENSE.txt for license information. |
| 6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #ifndef _LIBCPP_SYNCSTREAM |
| 11 | #define _LIBCPP_SYNCSTREAM |
| 12 | |
| 13 | /* |
| 14 | syncstream synopsis |
| 15 | |
| 16 | #include <ostream> // see [ostream.syn] |
| 17 | |
| 18 | namespace std { |
| 19 | template<class charT, class traits, class Allocator> |
| 20 | class basic_syncbuf; |
| 21 | |
| 22 | // [syncstream.syncbuf.special], specialized algorithms |
| 23 | template<class charT, class traits, class Allocator> |
| 24 | void swap(basic_syncbuf<charT, traits, Allocator>&, |
| 25 | basic_syncbuf<charT, traits, Allocator>&); |
| 26 | |
| 27 | using syncbuf = basic_syncbuf<char>; |
| 28 | using wsyncbuf = basic_syncbuf<wchar_t>; |
| 29 | |
| 30 | template<class charT, class traits, class Allocator> |
| 31 | class basic_osyncstream; |
| 32 | |
| 33 | using osyncstream = basic_osyncstream<char>; |
| 34 | using wosyncstream = basic_osyncstream<wchar_t>; |
| 35 | |
| 36 | template<class charT, class traits, class Allocator> |
| 37 | class basic_syncbuf : public basic_streambuf<charT, traits> { |
| 38 | public: |
| 39 | using char_type = charT; |
| 40 | using int_type = typename traits::int_type; |
| 41 | using pos_type = typename traits::pos_type; |
| 42 | using off_type = typename traits::off_type; |
| 43 | using traits_type = traits; |
| 44 | using allocator_type = Allocator; |
| 45 | |
| 46 | using streambuf_type = basic_streambuf<charT, traits>; |
| 47 | |
| 48 | // [syncstream.syncbuf.cons], construction and destruction |
| 49 | basic_syncbuf() |
| 50 | : basic_syncbuf(nullptr) {} |
| 51 | explicit basic_syncbuf(streambuf_type* obuf) |
| 52 | : basic_syncbuf(obuf, Allocator()) {} |
| 53 | basic_syncbuf(streambuf_type*, const Allocator&); |
| 54 | basic_syncbuf(basic_syncbuf&&); |
| 55 | ~basic_syncbuf(); |
| 56 | |
| 57 | // [syncstream.syncbuf.assign], assignment and swap |
| 58 | basic_syncbuf& operator=(basic_syncbuf&&); |
| 59 | void swap(basic_syncbuf&); |
| 60 | |
| 61 | // [syncstream.syncbuf.members], member functions |
| 62 | bool emit(); |
| 63 | streambuf_type* get_wrapped() const noexcept; |
| 64 | allocator_type get_allocator() const noexcept; |
| 65 | void set_emit_on_sync(bool) noexcept; |
| 66 | |
| 67 | protected: |
| 68 | // [syncstream.syncbuf.virtuals], overridden virtual functions |
| 69 | int sync() override; |
| 70 | |
| 71 | private: |
| 72 | streambuf_type* wrapped; // exposition only |
| 73 | bool emit_on_sync{}; // exposition only |
| 74 | }; |
| 75 | |
| 76 | // [syncstream.syncbuf.special], specialized algorithms |
| 77 | template<class charT, class traits, class Allocator> |
| 78 | void swap(basic_syncbuf<charT, traits, Allocator>&, |
| 79 | basic_syncbuf<charT, traits, Allocator>&); |
| 80 | |
| 81 | template<class charT, class traits, class Allocator> |
| 82 | class basic_osyncstream : public basic_ostream<charT, traits> { |
| 83 | public: |
| 84 | using char_type = charT; |
| 85 | using int_type = typename traits::int_type; |
| 86 | using pos_type = typename traits::pos_type; |
| 87 | using off_type = typename traits::off_type; |
| 88 | using traits_type = traits; |
| 89 | |
| 90 | using allocator_type = Allocator; |
| 91 | using streambuf_type = basic_streambuf<charT, traits>; |
| 92 | using syncbuf_type = basic_syncbuf<charT, traits, Allocator>; |
| 93 | |
| 94 | // [syncstream.osyncstream.cons], construction and destruction |
| 95 | basic_osyncstream(streambuf_type*, const Allocator&); |
| 96 | explicit basic_osyncstream(streambuf_type* obuf) |
| 97 | : basic_osyncstream(obuf, Allocator()) {} |
| 98 | basic_osyncstream(basic_ostream<charT, traits>& os, const Allocator& allocator) |
| 99 | : basic_osyncstream(os.rdbuf(), allocator) {} |
| 100 | explicit basic_osyncstream(basic_ostream<charT, traits>& os) |
| 101 | : basic_osyncstream(os, Allocator()) {} |
| 102 | basic_osyncstream(basic_osyncstream&&) noexcept; |
| 103 | ~basic_osyncstream(); |
| 104 | |
| 105 | // [syncstream.osyncstream.assign], assignment |
| 106 | basic_osyncstream& operator=(basic_osyncstream&&); |
| 107 | |
| 108 | // [syncstream.osyncstream.members], member functions |
| 109 | void emit(); |
| 110 | streambuf_type* get_wrapped() const noexcept; |
| 111 | syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); } |
| 112 | |
| 113 | private: |
| 114 | syncbuf_type sb; // exposition only |
| 115 | }; |
| 116 | } |
| 117 | |
| 118 | */ |
| 119 | |
| 120 | #if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) |
| 121 | # include <__cxx03/__config> |
| 122 | #else |
| 123 | # include <__config> |
| 124 | |
| 125 | // standard-mandated includes |
| 126 | |
| 127 | // [syncstream.syn] |
| 128 | # include <ostream> |
| 129 | |
| 130 | # if _LIBCPP_HAS_LOCALIZATION |
| 131 | |
| 132 | # include <__mutex/lock_guard.h> |
| 133 | # include <__utility/move.h> |
| 134 | # include <ios> |
| 135 | # include <iosfwd> // required for declaration of default arguments |
| 136 | # include <streambuf> |
| 137 | # include <string> |
| 138 | # if _LIBCPP_HAS_THREADS |
| 139 | # include <map> |
| 140 | # include <shared_mutex> |
| 141 | # endif |
| 142 | |
| 143 | # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
| 144 | # pragma GCC system_header |
| 145 | # endif |
| 146 | |
| 147 | _LIBCPP_PUSH_MACROS |
| 148 | # include <__undef_macros> |
| 149 | |
| 150 | _LIBCPP_BEGIN_NAMESPACE_STD |
| 151 | |
| 152 | # if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM |
| 153 | |
| 154 | // [syncstream.syncbuf.overview]/1 |
| 155 | // Class template basic_syncbuf stores character data written to it, |
| 156 | // known as the associated output, into internal buffers allocated |
| 157 | // using the object's allocator. The associated output is transferred |
| 158 | // to the wrapped stream buffer object *wrapped when emit() is called |
| 159 | // or when the basic_syncbuf object is destroyed. Such transfers are |
| 160 | // atomic with respect to transfers by other basic_syncbuf objects |
| 161 | // with the same wrapped stream buffer object. |
| 162 | // |
| 163 | // This helper singleton is used to implement the required |
| 164 | // synchronisation guarantees. |
| 165 | # if _LIBCPP_HAS_THREADS |
| 166 | class __wrapped_streambuf_mutex { |
| 167 | _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex() = default; |
| 168 | |
| 169 | public: |
| 170 | __wrapped_streambuf_mutex(const __wrapped_streambuf_mutex&) = delete; |
| 171 | __wrapped_streambuf_mutex& operator=(const __wrapped_streambuf_mutex&) = delete; |
| 172 | |
| 173 | _LIBCPP_HIDE_FROM_ABI void __inc_reference([[maybe_unused]] void* __ptr) { |
| 174 | _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); |
| 175 | unique_lock __lock{__mutex_}; |
| 176 | ++__lut_[reinterpret_cast<uintptr_t>(__ptr)].__count; |
| 177 | } |
| 178 | |
| 179 | // pre: __ptr is in __lut_ |
| 180 | _LIBCPP_HIDE_FROM_ABI void __dec_reference([[maybe_unused]] void* __ptr) noexcept { |
| 181 | unique_lock __lock{__mutex_}; |
| 182 | |
| 183 | auto __it = __get_it(__ptr); |
| 184 | if (__it->second.__count == 1) |
| 185 | __lut_.erase(__it); |
| 186 | else |
| 187 | --__it->second.__count; |
| 188 | } |
| 189 | |
| 190 | // TODO |
| 191 | // This function causes emit() aquire two mutexes: |
| 192 | // - __mutex_ shared |
| 193 | // _ __get_it(__ptr)->second.__mutex exclusive |
| 194 | // |
| 195 | // Instead store a pointer to __get_it(__ptr)->second.__mutex when |
| 196 | // calling __inc_reference. |
| 197 | // |
| 198 | // pre: __ptr is in __lut_ |
| 199 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI lock_guard<mutex> __get_lock([[maybe_unused]] void* __ptr) noexcept { |
| 200 | shared_lock __lock{__mutex_}; |
| 201 | return lock_guard{__get_it(__ptr)->second.__mutex}; |
| 202 | } |
| 203 | |
| 204 | // This function is used for testing. |
| 205 | // |
| 206 | // It is allowed to call this function with a non-registered pointer. |
| 207 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __get_count([[maybe_unused]] void* __ptr) noexcept { |
| 208 | _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); |
| 209 | shared_lock __lock{__mutex_}; |
| 210 | |
| 211 | auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr)); |
| 212 | return __it != __lut_.end() ? __it->second.__count : 0; |
| 213 | } |
| 214 | |
| 215 | [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI __wrapped_streambuf_mutex& __instance() noexcept { |
| 216 | static __wrapped_streambuf_mutex __result; |
| 217 | return __result; |
| 218 | } |
| 219 | |
| 220 | private: |
| 221 | struct __value { |
| 222 | mutex __mutex; |
| 223 | size_t __count{0}; |
| 224 | }; |
| 225 | |
| 226 | shared_mutex __mutex_; |
| 227 | map<uintptr_t, __value> __lut_; |
| 228 | |
| 229 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI map<uintptr_t, __value>::iterator __get_it(void* __ptr) noexcept { |
| 230 | _LIBCPP_ASSERT_INTERNAL(__ptr != nullptr, "non-wrapped streambufs are never written to"); |
| 231 | |
| 232 | auto __it = __lut_.find(reinterpret_cast<uintptr_t>(__ptr)); |
| 233 | _LIBCPP_ASSERT_INTERNAL(__it != __lut_.end(), "using a wrapped streambuf that has not been registered"); |
| 234 | _LIBCPP_ASSERT_INTERNAL(__it->second.__count >= 1, "found an inactive streambuf wrapper"); |
| 235 | return __it; |
| 236 | } |
| 237 | }; |
| 238 | # endif // _LIBCPP_HAS_THREADS |
| 239 | |
| 240 | // basic_syncbuf |
| 241 | |
| 242 | // The class uses a basic_string<_CharT, _Traits, _Allocator> as |
| 243 | // internal buffer. Per [syncstream.syncbuf.cons]/4 |
| 244 | // Remarks: A copy of allocator is used to allocate memory for |
| 245 | // internal buffers holding the associated output. |
| 246 | // |
| 247 | // Therefore the allocator used in the constructor is passed to the |
| 248 | // basic_string. The class does not keep a copy of this allocator. |
| 249 | template <class _CharT, class _Traits, class _Allocator> |
| 250 | class basic_syncbuf : public basic_streambuf<_CharT, _Traits> { |
| 251 | public: |
| 252 | using char_type = _CharT; |
| 253 | using traits_type = _Traits; |
| 254 | using int_type = typename traits_type::int_type; |
| 255 | using pos_type = typename traits_type::pos_type; |
| 256 | using off_type = typename traits_type::off_type; |
| 257 | using allocator_type = _Allocator; |
| 258 | |
| 259 | using streambuf_type = basic_streambuf<_CharT, _Traits>; |
| 260 | |
| 261 | // [syncstream.syncbuf.cons], construction and destruction |
| 262 | |
| 263 | _LIBCPP_HIDE_FROM_ABI basic_syncbuf() : basic_syncbuf(nullptr) {} |
| 264 | |
| 265 | _LIBCPP_HIDE_FROM_ABI explicit basic_syncbuf(streambuf_type* __obuf) : basic_syncbuf(__obuf, _Allocator()) {} |
| 266 | |
| 267 | _LIBCPP_HIDE_FROM_ABI basic_syncbuf(streambuf_type* __obuf, _Allocator const& __alloc) |
| 268 | : __wrapped_(__obuf), __str_(__alloc) { |
| 269 | __inc_reference(); |
| 270 | } |
| 271 | |
| 272 | _LIBCPP_HIDE_FROM_ABI basic_syncbuf(basic_syncbuf&& __other) |
| 273 | : __wrapped_(__other.get_wrapped()), __str_(std::move(__other.__str_)), __emit_on_sync_(__other.__emit_on_sync_) { |
| 274 | __move_common(__other); |
| 275 | } |
| 276 | |
| 277 | _LIBCPP_HIDE_FROM_ABI ~basic_syncbuf() { |
| 278 | # if _LIBCPP_HAS_EXCEPTIONS |
| 279 | try { |
| 280 | # endif // _LIBCPP_HAS_EXCEPTIONS |
| 281 | emit(); |
| 282 | # if _LIBCPP_HAS_EXCEPTIONS |
| 283 | } catch (...) { |
| 284 | } |
| 285 | # endif // _LIBCPP_HAS_EXCEPTIONS |
| 286 | __dec_reference(); |
| 287 | } |
| 288 | |
| 289 | // [syncstream.syncbuf.assign], assignment and swap |
| 290 | |
| 291 | _LIBCPP_HIDE_FROM_ABI basic_syncbuf& operator=(basic_syncbuf&& __other) { |
| 292 | // The function is specified to call emit. This call should |
| 293 | // propagate the exception thrown. |
| 294 | emit(); |
| 295 | __dec_reference(); |
| 296 | |
| 297 | __wrapped_ = __other.get_wrapped(); |
| 298 | __str_ = std::move(__other.__str_); |
| 299 | __emit_on_sync_ = __other.__emit_on_sync_; |
| 300 | |
| 301 | __move_common(__other); |
| 302 | |
| 303 | return *this; |
| 304 | } |
| 305 | |
| 306 | _LIBCPP_HIDE_FROM_ABI void swap(basic_syncbuf& __other) { |
| 307 | _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR( |
| 308 | allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(), |
| 309 | "violates the mandated swap precondition"); |
| 310 | |
| 311 | basic_syncbuf __tmp(std::move(__other)); |
| 312 | __other = std::move(*this); |
| 313 | *this = std::move(__tmp); |
| 314 | } |
| 315 | |
| 316 | // [syncstream.syncbuf.members], member functions |
| 317 | |
| 318 | _LIBCPP_HIDE_FROM_ABI bool emit() { return emit(false); } |
| 319 | |
| 320 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __wrapped_; } |
| 321 | |
| 322 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI allocator_type get_allocator() const noexcept { return __str_.get_allocator(); } |
| 323 | |
| 324 | _LIBCPP_HIDE_FROM_ABI void set_emit_on_sync(bool __b) noexcept { __emit_on_sync_ = __b; } |
| 325 | |
| 326 | protected: |
| 327 | // [syncstream.syncbuf.virtuals], overridden virtual functions |
| 328 | |
| 329 | _LIBCPP_HIDE_FROM_ABI_VIRTUAL |
| 330 | int sync() override { |
| 331 | if (__emit_on_sync_ && !emit(true)) |
| 332 | return -1; |
| 333 | return 0; |
| 334 | } |
| 335 | |
| 336 | _LIBCPP_HIDE_FROM_ABI_VIRTUAL |
| 337 | int_type overflow(int_type __c = traits_type::eof()) override { |
| 338 | if (traits_type::eq_int_type(__c, traits_type::eof())) |
| 339 | return traits_type::not_eof(__c); |
| 340 | |
| 341 | if (this->pptr() == this->epptr()) { |
| 342 | # if _LIBCPP_HAS_EXCEPTIONS |
| 343 | try { |
| 344 | # endif |
| 345 | size_t __size = __str_.size(); |
| 346 | __str_.resize(__str_.capacity() + 1); |
| 347 | _LIBCPP_ASSERT_INTERNAL(__str_.size() > __size, "the buffer hasn't grown"); |
| 348 | |
| 349 | char_type* __p = static_cast<char_type*>(__str_.data()); |
| 350 | this->setp(__p, __p + __str_.size()); |
| 351 | this->pbump(__size); |
| 352 | |
| 353 | # if _LIBCPP_HAS_EXCEPTIONS |
| 354 | } catch (...) { |
| 355 | return traits_type::eof(); |
| 356 | } |
| 357 | # endif |
| 358 | } |
| 359 | |
| 360 | return this->sputc(traits_type::to_char_type(__c)); |
| 361 | } |
| 362 | |
| 363 | private: |
| 364 | streambuf_type* __wrapped_; |
| 365 | |
| 366 | // TODO Use a more generic buffer. |
| 367 | // That buffer should be light with almost no additional headers. Then |
| 368 | // it can be use here, the __retarget_buffer, and place that use |
| 369 | // the now removed get_temporary_buffer |
| 370 | |
| 371 | basic_string<_CharT, _Traits, _Allocator> __str_; |
| 372 | bool __emit_on_sync_{false}; |
| 373 | |
| 374 | _LIBCPP_HIDE_FROM_ABI bool emit(bool __flush) { |
| 375 | if (!__wrapped_) |
| 376 | return false; |
| 377 | |
| 378 | # if _LIBCPP_HAS_THREADS |
| 379 | lock_guard<mutex> __lock = __wrapped_streambuf_mutex::__instance().__get_lock(__wrapped_); |
| 380 | # endif |
| 381 | |
| 382 | bool __result = true; |
| 383 | if (this->pptr() != this->pbase()) { |
| 384 | _LIBCPP_ASSERT_INTERNAL(this->pbase() && this->pptr() && this->epptr(), "all put area pointers shold be valid"); |
| 385 | |
| 386 | // The __str_ does not know how much of its buffer is used. This |
| 387 | // information is extracted from the information of the base class. |
| 388 | __result &= (__wrapped_->sputn(this->pbase(), this->pptr() - this->pbase()) != -1); |
| 389 | // Clears the buffer, but keeps the contents (and) size of the |
| 390 | // internal buffer. |
| 391 | this->setp(this->pbase(), this->epptr()); |
| 392 | } |
| 393 | |
| 394 | if (__flush) |
| 395 | __result &= (__wrapped_->pubsync() != -1); |
| 396 | |
| 397 | return __result; |
| 398 | } |
| 399 | |
| 400 | _LIBCPP_HIDE_FROM_ABI void __move_common(basic_syncbuf& __other) { |
| 401 | // Adjust the put area pointers to our buffer. |
| 402 | char_type* __p = static_cast<char_type*>(__str_.data()); |
| 403 | this->setp(__p, __p + __str_.size()); |
| 404 | this->pbump(__other.pptr() - __other.pbase()); |
| 405 | |
| 406 | // Clear __other_ so the destructor will act as a NOP. |
| 407 | __other.setp(nullptr, nullptr); |
| 408 | __other.__wrapped_ = nullptr; |
| 409 | } |
| 410 | |
| 411 | _LIBCPP_HIDE_FROM_ABI void __inc_reference() { |
| 412 | # if _LIBCPP_HAS_THREADS |
| 413 | if (__wrapped_) |
| 414 | __wrapped_streambuf_mutex::__instance().__inc_reference(__wrapped_); |
| 415 | # endif |
| 416 | } |
| 417 | |
| 418 | _LIBCPP_HIDE_FROM_ABI void __dec_reference() noexcept { |
| 419 | # if _LIBCPP_HAS_THREADS |
| 420 | if (__wrapped_) |
| 421 | __wrapped_streambuf_mutex::__instance().__dec_reference(__wrapped_); |
| 422 | # endif |
| 423 | } |
| 424 | }; |
| 425 | |
| 426 | using std::syncbuf; |
| 427 | # if _LIBCPP_HAS_WIDE_CHARACTERS |
| 428 | using std::wsyncbuf; |
| 429 | # endif |
| 430 | |
| 431 | // [syncstream.syncbuf.special], specialized algorithms |
| 432 | template <class _CharT, class _Traits, class _Allocator> |
| 433 | _LIBCPP_HIDE_FROM_ABI void |
| 434 | swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __lhs, basic_syncbuf<_CharT, _Traits, _Allocator>& __rhs) { |
| 435 | __lhs.swap(__rhs); |
| 436 | } |
| 437 | |
| 438 | // basic_osyncstream |
| 439 | |
| 440 | template <class _CharT, class _Traits, class _Allocator> |
| 441 | class basic_osyncstream : public basic_ostream<_CharT, _Traits> { |
| 442 | public: |
| 443 | using char_type = _CharT; |
| 444 | using traits_type = _Traits; |
| 445 | using int_type = typename traits_type::int_type; |
| 446 | using pos_type = typename traits_type::pos_type; |
| 447 | using off_type = typename traits_type::off_type; |
| 448 | |
| 449 | using allocator_type = _Allocator; |
| 450 | using streambuf_type = basic_streambuf<char_type, traits_type>; |
| 451 | using syncbuf_type = basic_syncbuf<char_type, traits_type, allocator_type>; |
| 452 | |
| 453 | // [syncstream.osyncstream.cons], construction and destruction |
| 454 | |
| 455 | _LIBCPP_HIDE_FROM_ABI basic_osyncstream(streambuf_type* __obuf, allocator_type const& __alloc) |
| 456 | : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(__obuf, __alloc) {} |
| 457 | |
| 458 | _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(streambuf_type* __obuf) |
| 459 | : basic_osyncstream(__obuf, allocator_type()) {} |
| 460 | |
| 461 | _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_ostream<char_type, traits_type>& __os, allocator_type const& __alloc) |
| 462 | : basic_osyncstream(__os.rdbuf(), __alloc) {} |
| 463 | |
| 464 | _LIBCPP_HIDE_FROM_ABI explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os) |
| 465 | : basic_osyncstream(__os, allocator_type()) {} |
| 466 | |
| 467 | _LIBCPP_HIDE_FROM_ABI basic_osyncstream(basic_osyncstream&& __other) noexcept |
| 468 | : basic_ostream<_CharT, _Traits>(std::addressof(__sb_)), __sb_(std::move(__other.__sb_)) { |
| 469 | this->set_rdbuf(std::addressof(__sb_)); |
| 470 | } |
| 471 | |
| 472 | // [syncstream.osyncstream.assign], assignment |
| 473 | |
| 474 | _LIBCPP_HIDE_FROM_ABI basic_osyncstream& operator=(basic_osyncstream&& __other) = default; |
| 475 | |
| 476 | // [syncstream.osyncstream.members], member functions |
| 477 | |
| 478 | _LIBCPP_HIDE_FROM_ABI void emit() { |
| 479 | // The basic_ostream::put places the sentry in a try |
| 480 | // catch, this does not match the wording of the standard |
| 481 | // [ostream.unformatted] |
| 482 | // TODO validate other unformatted output functions. |
| 483 | typename basic_ostream<char_type, traits_type>::sentry __s(*this); |
| 484 | if (__s) { |
| 485 | # if _LIBCPP_HAS_EXCEPTIONS |
| 486 | try { |
| 487 | # endif |
| 488 | |
| 489 | if (__sb_.emit() == false) |
| 490 | this->setstate(ios::badbit); |
| 491 | # if _LIBCPP_HAS_EXCEPTIONS |
| 492 | } catch (...) { |
| 493 | this->__set_badbit_and_consider_rethrow(); |
| 494 | } |
| 495 | # endif |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI streambuf_type* get_wrapped() const noexcept { return __sb_.get_wrapped(); } |
| 500 | |
| 501 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI syncbuf_type* rdbuf() const noexcept { |
| 502 | return const_cast<syncbuf_type*>(std::addressof(__sb_)); |
| 503 | } |
| 504 | |
| 505 | private: |
| 506 | syncbuf_type __sb_; |
| 507 | }; |
| 508 | |
| 509 | using std::osyncstream; |
| 510 | # if _LIBCPP_HAS_WIDE_CHARACTERS |
| 511 | using std::wosyncstream; |
| 512 | # endif |
| 513 | |
| 514 | # endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM |
| 515 | |
| 516 | _LIBCPP_END_NAMESPACE_STD |
| 517 | |
| 518 | _LIBCPP_POP_MACROS |
| 519 | |
| 520 | # endif // _LIBCPP_HAS_LOCALIZATION |
| 521 | #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS) |
| 522 | |
| 523 | #endif // _LIBCPP_SYNCSTREAM |