29 Input/output library [input.output]

29.10 Synchronized output streams [syncstream]

29.10.1 Header <syncstream> synopsis [syncstream.syn]

#include <ostream>  // see [ostream.syn]

namespace std {
  template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
    class basic_syncbuf;

  using syncbuf = basic_syncbuf<char>;
  using wsyncbuf = basic_syncbuf<wchar_t>;

  template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
    class basic_osyncstream;

  using osyncstream = basic_osyncstream<char>;
  using wosyncstream = basic_osyncstream<wchar_t>;
}
The header <syncstream> provides a mechanism to synchronize execution agents writing to the same stream.

29.10.2 Class template basic_­syncbuf [syncstream.syncbuf]

29.10.2.1 Overview [syncstream.syncbuf.overview]

namespace std {
  template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
  class basic_syncbuf : public basic_streambuf<charT, traits> {
  public:
    using char_type      = charT;
    using int_type       = typename traits::int_type;
    using pos_type       = typename traits::pos_type;
    using off_type       = typename traits::off_type;
    using traits_type    = traits;
    using allocator_type = Allocator;

    using streambuf_type = basic_streambuf<charT, traits>;

    // [syncstream.syncbuf.cons], construction and destruction
    basic_syncbuf()
      : basic_syncbuf(nullptr) {}
    explicit basic_syncbuf(streambuf_type* obuf)
      : basic_syncbuf(obuf, Allocator()) {}
    basic_syncbuf(streambuf_type*, const Allocator&);
    basic_syncbuf(basic_syncbuf&&);
    ~basic_syncbuf();

    // [syncstream.syncbuf.assign], assignment and swap
    basic_syncbuf& operator=(basic_syncbuf&&);
    void swap(basic_syncbuf&);

    // [syncstream.syncbuf.members], member functions
    bool emit();
    streambuf_type* get_wrapped() const noexcept;
    allocator_type get_allocator() const noexcept;
    void set_emit_on_sync(bool) noexcept;

  protected:
    // [syncstream.syncbuf.virtuals], overridden virtual functions
    int sync() override;

  private:
    streambuf_type* wrapped;    // exposition only
    bool emit_on_sync{};        // exposition only
  };

  // [syncstream.syncbuf.special], specialized algorithms
  template<class charT, class traits, class Allocator>
    void swap(basic_syncbuf<charT, traits, Allocator>&,
              basic_syncbuf<charT, traits, Allocator>&);
}
Class template basic_­syncbuf stores character data written to it, known as the associated output, into internal buffers allocated using the object's allocator.
The associated output is transferred to the wrapped stream buffer object *wrapped when emit() is called or when the basic_­syncbuf object is destroyed.
Such transfers are atomic with respect to transfers by other basic_­syncbuf objects with the same wrapped stream buffer object.

29.10.2.2 Construction and destruction [syncstream.syncbuf.cons]

basic_syncbuf(streambuf_type* obuf, const Allocator& allocator);
Effects: Sets wrapped to obuf.
Remarks: A copy of allocator is used to allocate memory for internal buffers holding the associated output.
Throws: Nothing unless an exception is thrown by the construction of a mutex or by memory allocation.
Postconditions: get_­wrapped() == obuf and get_­allocator() == allocator are true.
basic_syncbuf(basic_syncbuf&& other);
Postconditions: The value returned by this->get_­wrapped() is the value returned by other.get_­wrapped() prior to calling this constructor.
Output stored in other prior to calling this constructor will be stored in *this afterwards.
other.rdbuf()->pbase() == other.rdbuf()->pptr() and other.get_­wrapped() == nullptr are true.
Remarks: This constructor disassociates other from its wrapped stream buffer, ensuring destruction of other produces no output.
~basic_syncbuf();
Effects: Calls emit().
Throws: Nothing.
If an exception is thrown from emit(), the destructor catches and ignores that exception.

29.10.2.3 Assignment and swap [syncstream.syncbuf.assign]

basic_syncbuf& operator=(basic_syncbuf&& rhs) noexcept;
Effects: Calls emit() then move assigns from rhs.
After the move assignment *this has the observable state it would have had if it had been move constructed from rhs ([syncstream.syncbuf.cons]).
Returns: *this.
Postconditions:
  • rhs.get_­wrapped() == nullptr is true.
  • this->get_­allocator() == rhs.get_­allocator() is true when
    allocator_traits<Allocator>::propagate_on_container_move_assignment::value
    
    is true; otherwise, the allocator is unchanged.
Remarks: This assignment operator disassociates rhs from its wrapped stream buffer, ensuring destruction of rhs produces no output.
void swap(basic_syncbuf& other) noexcept;
Preconditions: Either allocator_­traits<Allocator>​::​propagate_­on_­container_­swap​::​value is true or this->get_­allocator() == other.get_­allocator() is true.
Effects: Exchanges the state of *this and other.

29.10.2.4 Member functions [syncstream.syncbuf.members]

bool emit();
Effects: Atomically transfers the associated output of *this to the stream buffer *wrapped, so that it appears in the output stream as a contiguous sequence of characters.
wrapped->pubsync() is called if and only if a call was made to sync() since the most recent call to emit(), if any.
Returns: true if all of the following conditions hold; otherwise false:
  • wrapped == nullptr is false.
  • All of the characters in the associated output were successfully transferred.
  • The call to wrapped->pubsync() (if any) succeeded.
Postconditions: On success, the associated output is empty.
Synchronization: All emit() calls transferring characters to the same stream buffer object appear to execute in a total order consistent with the β€œhappens before” relation ([intro.races]), where each emit() call synchronizes with subsequent emit() calls in that total order.
Remarks: May call member functions of wrapped while holding a lock uniquely associated with wrapped.
streambuf_type* get_wrapped() const noexcept;
Returns: wrapped.
allocator_type get_allocator() const noexcept;
Returns: A copy of the allocator that was set in the constructor or assignment operator.
void set_emit_on_sync(bool b) noexcept;
Effects: emit_­on_­sync = b.

29.10.2.5 Overridden virtual functions [syncstream.syncbuf.virtuals]

int sync() override;
Effects: Records that the wrapped stream buffer is to be flushed.
Then, if emit_­on_­sync is true, calls emit().
[Note
:
If emit_­on_­sync is false, the actual flush is delayed until a call to emit().
β€” end note
]
Returns: If emit() was called and returned false, returns -1; otherwise 0.

29.10.2.6 Specialized algorithms [syncstream.syncbuf.special]

template<class charT, class traits, class Allocator> void swap(basic_syncbuf<charT, traits, Allocator>& a, basic_syncbuf<charT, traits, Allocator>& b) noexcept;
Effects: Equivalent to a.swap(b).

29.10.3 Class template basic_­osyncstream [syncstream.osyncstream]

29.10.3.1 Overview [syncstream.osyncstream.overview]

namespace std {
  template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
  class basic_osyncstream : public basic_ostream<charT, traits> {
  public:
    using char_type   = charT;
    using int_type    = typename traits::int_type;
    using pos_type    = typename traits::pos_type;
    using off_type    = typename traits::off_type;
    using traits_type = traits;

    using allocator_type = Allocator;
    using streambuf_type = basic_streambuf<charT, traits>;
    using syncbuf_type   = basic_syncbuf<charT, traits, Allocator>;

    // [syncstream.osyncstream.cons], construction and destruction
    basic_osyncstream(streambuf_type*, const Allocator&);
    explicit basic_osyncstream(streambuf_type* obuf)
      : basic_osyncstream(obuf, Allocator()) {}
    basic_osyncstream(basic_ostream<charT, traits>& os, const Allocator& allocator)
      : basic_osyncstream(os.rdbuf(), allocator) {}
    explicit basic_osyncstream(basic_ostream<charT, traits>& os)
      : basic_osyncstream(os, Allocator()) {}
    basic_osyncstream(basic_osyncstream&&) noexcept;
    ~basic_osyncstream();

    // assignment
    basic_osyncstream& operator=(basic_osyncstream&&) noexcept;

    // [syncstream.osyncstream.members], member functions
    void emit();
    streambuf_type* get_wrapped() const noexcept;
    syncbuf_type* rdbuf() const noexcept { return const_cast<syncbuf_type*>(addressof(sb)); }

  private:
    syncbuf_type sb;    // exposition only
  };
}
Allocator shall meet the Cpp17Allocator requirements (Table 36).
[Example
:
A named variable can be used within a block statement for streaming.
{
  osyncstream bout(cout);
  bout << "Hello, ";
  bout << "World!";
  bout << endl; // flush is noted
  bout << "and more!\n";
}   // characters are transferred and cout is flushed
β€” end example
]
[Example
:
A temporary object can be used for streaming within a single statement.
osyncstream(cout) << "Hello, " << "World!" << '\n';
In this example, cout is not flushed.
β€” end example
]

29.10.3.2 Construction and destruction [syncstream.osyncstream.cons]

basic_osyncstream(streambuf_type* buf, const Allocator& allocator);
Effects: Initializes sb from buf and allocator.
Initializes the base class with basic_­ostream<charT, traits>(addressof(sb)).
[Note
:
The member functions of the provided stream buffer might be called from emit() while a lock is held.
Care should be taken to ensure that this does not result in deadlock.
β€” end note
]
Postconditions: get_­wrapped() == buf is true.
basic_osyncstream(basic_osyncstream&& other) noexcept;
Effects: Move constructs the base class and sb from the corresponding subobjects of other, and calls basic_­ostream<charT, traits>​::​set_­rdbuf(addressof(sb)).
Postconditions: The value returned by get_­wrapped() is the value returned by os.get_­wrapped() prior to calling this constructor.
nullptr == other.get_­wrapped() is true.

29.10.3.3 Member functions [syncstream.osyncstream.members]

void emit();
Effects: Calls sb.emit().
If that call returns false, calls setstate(ios_­base​::​badbit).
[Example
:
A flush on a basic_­osyncstream does not flush immediately:
{
  osyncstream bout(cout);
  bout << "Hello," << '\n';     // no flush
  bout.emit();                  // characters transferred; cout not flushed
  bout << "World!" << endl;     // flush noted; cout not flushed
  bout.emit();                  // characters transferred; cout flushed
  bout << "Greetings." << '\n'; // no flush
}   // characters transferred; cout not flushed
β€” end example
]
[Example
:
The function emit() can be used to handle exceptions from operations on the underlying stream.
{
  osyncstream bout(cout);
  bout << "Hello, " << "World!" << '\n';
  try {
    bout.emit();
  } catch (...) {
    // handle exception
  }
}
β€” end example
]
streambuf_type* get_wrapped() const noexcept;
Returns: sb.get_­wrapped().
[Example
:
Obtaining the wrapped stream buffer with get_­wrapped() allows wrapping it again with an osyncstream.
For example,
{
  osyncstream bout1(cout);
  bout1 << "Hello, ";
  {
    osyncstream(bout1.get_wrapped()) << "Goodbye, " << "Planet!" << '\n';
  }
  bout1 << "World!" << '\n';
}
produces the uninterleaved output
Goodbye, Planet!
Hello, World!
β€” end example
]