Search code examples
c++c++11istream

Improving error processing for an istream helper class when using exceptions


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
}

Solution

  • 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(); 
    }