I have written a class to control mandatory input on an istream, based on the original idea posted there: https://stackoverflow.com/a/14331519/3723423 . It verifies and skips mandatory formatting chars, a little bit like with scanf(), but with a stream semantic:
int country; string phone;
cin >> mandatory_input(" ( + ") >> country >> mandatory_input(" ) ") >> phone;
The class is consistent with standard istream operations, setting failbit in case of non compliant input, and throwing exceptions if and only if exceptions(istream::failbit);
was set.
I have questions for improving error processing when class is used with exceptions:
Alternative 1: put error information in a static member. That's my current solution.
Alternative 2: throw my own exception class derived from istream::failure
, with all information about error conditions. That would be the most elegant solution. Is there a way to throw my own exception AND set the failbit ? If I set failbit, an exception is thrown before I can throw my own. But if I don't set it, I'm not consistent with standard operations. (Edit: If I temporarily deactivate exceptions before I set faibit, the std exception is throws as soon as I reactiveate exceptions, again not giving me a chance to throw my own.)
Alternative 3: is it possible to set the error code that is thrown by standard exception when failbit is set ? Unfortunately, after having read about std::io_errc
and std::make_error_code()
, it's still not clear how I could make the failbit exception using my own error code.
Here the piece of code where error is set:
...
else if ((c = is.get()) != *p) { // input char not matching expected char
is.putback(c);
mandatory_input::read_error = c;
mandatory_input::expected = *p;
// <==== Here I would like to trigger my own exception or predefine error code thrown by standard exception
is.setstate(std::ios::failbit); // stop extracting
}
I was so concentrated on searching std functions to solve that, that I didn't think of the most obvious solution: hijacking the std exception thrown. In case it could help s.o. else:
I first defined a dedicated nested failure class:
class mandatory_input {
public:
...
class failure : public std::istream::failure {
public:
failure(std::error_code e);
};
};
Then I have added the following bloc in the original error processing code (see question):
// start of enhanced exception handling
if (is.exceptions() & std::istream::failbit) { // if exception will be thrown
try {
is.setstate(std::ios::failbit);
} catch (std::istream::failure &e) { // the failbit will trigger this
throw mandatory_input::failure(e.code()); // and i throw my own
} catch (std::exception &e) { // just in case other low-level errors would be thrown
throw e;
}
} else //======= end of enhanced exceptions handling
Now with this solution, the clients of my helper class who want to use .exceptions()
can process errors either undifferentiated:
try { cin >> mandatory_input(" ( + ") >> country >> .... ;
} catch (istream::failure e) {
cerr << "Input error: "<< e.code()<< " " << e.what();
}
or fine tune error processing:
try { ....
} catch (mandatory_input::failure e) {
cerr << "Input format mismatch: " << mandatory_input::getexpected()
<< " was expected, but " << mandatory_input::getread_error() << " was read !\n";
} catch (istream::failure e) {
cerr << "Input error: "<< e.code()<< " " << e.what();
}