>> And I wouldn't have the slightest clue how to return a "bad stream" in case they fail.
You shouldn't need to "do" anything. Your run-of-the-mill insertor/extractor ...
Code:
std::ostream & operator<< (std::ostream & out, const object & o )
{
out << o.foo();
out << o.bar();
return out;
}
If foo and bar return plain old data then there is no problem, the state of the stream is determined by the success or failure of the conversion. If foo and bar do not return plain old data then the state of the stream is determined by the result of operator << for the object.
It would be better if you understood that by default streams do not throw.
If you want a specific state to be exceptional you usually call exception( mask ); beforehand.