dimanche 22 février 2015

Synchronizing output to std::cout

I have an application wherein several threads write to std::cout.


In trying to figure out a simple method to synchronize their output (ie, no garbled lines) I came up with the following fairly simple implementation:



class syncstream : public std::ostringstream
{
public:
using std::ostringstream::ostringstream;

syncstream& operator<<(std::ostream& (*pf)(std::ostream&) ) { pf(*this); return *this; }
syncstream& operator<<(std::ios& (*pf)(std::ios&) ) { pf(*this); return *this; }
syncstream& operator<<(std::ios_base& (*pf)(std::ios_base&)) { pf(*this); return *this; }

template<typename T>
syncstream& operator<<(T&& token)
{
static_cast<std::ostringstream&>(*this) << std::forward<T>(token);
return *this;
}
};

inline std::ostream& operator&&(std::ostream& s, const syncstream& g) { return s << g.str(); }

#define synced(stream) stream && syncstream()


In a nutshell, the syncstream hides all the << operators and provides its own versions returning syncstream reference. This is needed to make the && operator (which has lower precedence than <<) work. Basically, the result of various << operators is accumulated in the syncstream and is then sent to the output stream as one string. Sorry about the macro.


So, now in my threads I can do:



synced(std::cout) << "Hello" << ' ' << "world" << ' ' << "from" << ' ' << "thread" << ' ' << n << std::endl;


I initially wrote the above because of my misunderstanding of §27.4.1. But, surprisingly it works very well.


I wrote this test case and ran it with both g++ 4.8.3 and clang++ 3.5.1 (with libstdc++ and libc++) using this script. And I cannot make it not work (ie, produce garbled lines).


So my questions are:



  1. Is my above implementation really thread-safe?

  2. a. If so, why?

  3. b. If not, why can't I "break" it? (Someone please try to "break" it.)


Other interesting (or not) facts:


a. Instead of writing my own class and overriding all the <<s, I at first thought I could simply do:



std::ostream& operator&&(std::ostream& s1, const std::ostream& s2) { return s1 << s2.rdbuf(); }
#define synced(stream) stream && std::stringstream()


But this didn't work and I was still getting garbled lines.


b. If I change std::ios_base::sync_with_stdio(true) to false the clang++ with libc++ test still passes, but all others either fail or segfault.


Aucun commentaire:

Enregistrer un commentaire