Here is the pattern for an extractor:
template<class charT, class Traits>
std::basic_istream<charT, Traits>&
operator>> (std::basic_istream<charT,
Traits>& is, UserDefinedType& x)
{
std::ios_base::iostate err = 0;
// Create a sentry object (may set error bits or throw failure)
typename basic_istream<charT, Traits>::sentry ipfx(is);
try {
// Proceed with I/O only if sentry is okay.
if(ipfx) {
// Typically you access the stream's locale or buffer.
// Don't call other stream I/O functions.
// Add state bits to the err variable if necessary,
// for example:
// if (...)
// err |= std::ios_base::failbit;
}
}
catch(...) {
bool flag = false;
try {
// set failbit (may throw failure)
is.setstate(std::ios_base::failbit);
}
catch(...) { flag = true; }
// Re-throw original exception (thrown from facet or buffer)
if (flag)
throw;
}
// As the last step, set error bits (may throw failure)
if (err)
is.setstate(err);
return is;
}
Similarly, the pattern for the inserter looks like this:
template<class charT, class Traits>
std::basic_ostream<charT, Traits>&
operator<<(std::basic_ostream<charT, Traits >& os,
const UserDefinedType& x)
{
std::ios_base::iostate err = 0;
// Create a sentry object (may set error bits or throw failure)
typename basic_ostream<charT, Traits>::sentry opfx(os);
try {
// Proceed with I/O only if sentry is okay
if (opfx) {
// Typically you access the stream's locale or buffer.
// Don't call other stream I/O functions
// Add state bits to the err variable if necessary,
// for example:
// if (...)
// err |= std::ios_base::failbit;
// Reset the field width after usage, that is,
// os.width(0);
}
} //try
catch(...) {
bool flag = false;
try {
// set failbit (may throw failure)
os.setstate(std::ios_base::failbit);
}
catch(...) {
// Catch exception, do not rethrow
flag = true;
}
// Rethrow original exception (from facet or buffer)
if (flag)
throw;
}
// As the last step, set error bits (may throw failure)
if (err)
os.setstate(err);
return os;
}