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:
- Is my above implementation really thread-safe?
- a. If so, why?
- 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