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; }