Catching exceptions

Discussion in 'C++' started by magnus.moraberg, May 11, 2009.

  1. Hi,

    I have the following incomplete code -

    File file;
    file.open(filePath);

    try
    {
    someOneElsesApi::startComplexProcessingOfFile(file);
    std::cout << "The file is now processed" << std::endl;
    }
    catch exception(/* all exceptions */)
    {
    std::cout << "The file didn't process because: " << std::endl;
    }

    startComplexProcessingOfFile() can throw a whole range of different
    exceptions.

    How do I catch them and inform the user in a useful manner. In python,
    there is a base exception class which can be used for this purpose. C#
    has inner exceptions which I also found quite useful.

    Thanks for your help,
     
    magnus.moraberg, May 11, 2009
    #1
    1. Advertisements

  2. You can use:
    catch(...)
    but it will catch all exceptions, and you will not know which one is caught.

    It is better to create all catch cases, and act accordingly. After all,
    you should know which exceptions are thrown, and what can be done for each.

    Something like:

    try
    {
    f(); // throws an exception
    }
    catch ( const exception1 &e )
    {
    // do 1
    }
    catch ( const exception2 &e )
    {
    // do 2
    }
    catch ( ... )
    {
    // ?
    }
     
    Vladimir Jovic, May 11, 2009
    #2
    1. Advertisements

  3. * :
    If the exceptions are all derived from some base class T (e.g. std::exception),
    just do

    try
    {
    // Whatever.
    }
    catch( std::exception const& x )
    {
    std::cerr << "!" << x.what() << std::endl;
    }

    If the exceptions are of unrelated types then you can do

    try
    {
    // Whatever.
    }
    catch( ... )
    {
    std::cerr << "!" << exceptionText() << std::endl;
    }

    where exceptionText is a routine that you have defined tailored to the exception
    types you expect, e.g. like

    std::string exceptionText()
    {
    try
    {
    throw;
    }
    catch( A const& a )
    {
    return exceptionTextFrom( a );
    }
    catch( B const& b )
    {
    return exceptionTextFrom( b )
    }
    catch( C const& c )
    {
    return exceptionTextFrom( c );
    }
    catch( std::exception const& x )
    {
    return x.what();
    }
    catch( ... )
    {
    return "Uknown exception";
    }
    }

    where exceptionTextFrom might be a routine that you define, or just direct code
    to access the exception object's message.

    For anything but the smallest program it is however generally a bad idea to
    present the message carried by an exception to the user as a main message. It's
    probably not adapted to the context it appears in. It's probably about internal
    technical details that the user doesn't care about and doesn't have access to.
    It's probably expressed in a technical terminology/jargon that the user doesn't
    understand. It may even be in a national language that user doesn't understand.
    Exception information is generally for programmers (e.g. log it).

    Cheers & hth,

    - Alf
     
    Alf P. Steinbach, May 11, 2009
    #3
  4. magnus.moraberg

    Jorgen Grahn Guest

    Do you use std::endl here because you explicitly want flushing of cout
    at this point, or because you mistakenly think \n is not portable?
    Not really. As far as I know, Python is like C++; you can throw any
    object. And even if there *was* such an Exception class, you would
    want to catch IOError in this case, to be able to tell the user
    something meaningful like "failed because 'foo.txt' is read
    protected".

    Returning to C++ ... if someOneElsesApi::startComplexProcessingOfFile()
    can throw "a whole range of different exceptions" and you have to care
    about more than two or three of them, I'd argue that it is broken.

    /Jorgen
     
    Jorgen Grahn, May 11, 2009
    #4
  5. magnus.moraberg

    James Kanze Guest

    Or most likely, because it is the standard way of terminating a
    line. (Standard in the sense of "usual or default practice".)
    At least from what he's posted, there's no reason to assume that
    the flush is causing a performance problem, so there's no reason
    to not use std::endl.
     
    James Kanze, May 12, 2009
    #5
  6. * James Kanze:
    It's also debatable whether

    #include <iostream>
    int main() { std::cout << "Bah\n"; }

    is guaranteed to produce any output at all.

    I don't know (though I suspect answer is "no") and don't care, but anyone
    recommending not using endl and criticizing others for using it should know and
    be able to back it up with some quote from the standard.


    Cheers,

    - Alf
     
    Alf P. Steinbach, May 12, 2009
    #6
  7. magnus.moraberg

    Jerry Coffin Guest

    I don't see how there's any room for debate at all. See [lib.ios::Init]
    (27.4.2.1.6). std::ios_base::Init is a class that (acts like it) keeps a
    reference count to ensure that cin, cout, cerr, and clog (and wide
    variants) are created once in a program that uses them, and flushed
    before the program exits.
     
    Jerry Coffin, May 12, 2009
    #7
  8. * Jerry Coffin:
    Of course, you can provide a quote or reference to the place the standard
    guarantees the creation of at least one such object?

    And, what about e.g. an ofstream?


    Cheers & hth.,

    - Alf
     
    Alf P. Steinbach, May 12, 2009
    #8
  9. magnus.moraberg

    Jerry Coffin Guest

    Sort of, in 27.3/2. That's what actually creates cin, cout, etc. -- so
    without it, the problem isn't going to be lack of output from the
    program -- it's going to be extra output from the linker saying your
    program has undefined externals, and then you'll get no executable at
    all.
    What about it? Do you mean: "Is all output that has been written to an
    fstream required to be written to the external file?" If so, the answer
    is basically yes. ~std::basic_filebuf() calls close() (§27.8.1.2/3).
    Close flushes the output by calling overflow(EOF) if a put area exists
    (§27.8.1.3/6). Of course, this assumes normal exit -- if the program
    exits via abort() (for one example) buffers aren't flushed.
     
    Jerry Coffin, May 12, 2009
    #9
  10. magnus.moraberg

    James Kanze Guest

    Since when? The standard guarantees that flush will be called
    on all of the standard stream objects during a clean shutdown.
    (There may be problems if you output to std::cout in the
    destructor of a static object. The stream is guaranteed not to
    be destructed, but you might have to manually ensure the flush
    to ensure that data is actually output.)

    Of course, one of my major arguments for using std::endl instead
    of '\n', by default, is that, like it or not, not all shutdowns
    are clean. And debugging is a lot easier if the output is
    actually indicative of how far the program has gotten.
    It's more an engineering issue than a standards one. Basically,
    std::endl is the "standard" way of terminating a line (standard
    in the sense of "usual" or "accepted practice", not in the sense
    of ISO/IEC 14882); replacing it with '\n' is an optimization.
    Which shouldn't be undertaken prematurely.
     
    James Kanze, May 13, 2009
    #10
  11. * Jerry Coffin:
    Thanks, I think that's the sought for guarantee as far as the in-practice is
    concerned, because it clearly demonstrates the intent.

    Sorry, no. There is an implication that placement new is used, since cout is a
    reference, so whether or not an Init object is ever created won't affect the
    things the linker sees. The question was more like whether the standard
    somewhere requires the use of the Init mechanism, and it seems 27.3/2 is it.

    Thanks again. In summary, the relevant references are 27.3/2, 27.8.1.2/3 and
    27.8.1.3/6. And although the formal is a bit lacking, as a matter of practice it
    seems there's no danger from "\n" (instead of endl) for a normal execution.


    Cheers,

    - Alf
     
    Alf P. Steinbach, May 13, 2009
    #11
  12. * James Kanze:
    That would be late 1998. :)

    Before the standardization it was even worse.

    E.g. Scott Meyers discovered that the actual workings and guarantees about
    buffering, endl etc., in the draft standard, were so vague and counter intuitive
    that he had to omit an item about preferentially using "\n" in Effective C++.

    I've yet to see that formal guarantee demonstrated.

    It seems that it's in the class of "the standard guarantees that <iostream> is
    enough to use cout, << etc." (oops, we forgot to actually state that!), which
    you helped to have fixed in C++0x.

    However, else-thread Jerry refers to 27.3/2, and as a matter of practicality to
    me that is convincing enough about the standard's authors' *intention*, namely
    that the standard is intended to require initialization of the standard iostream
    objects via the basic_ios::Init mechanism, which in turn provides flushing
    guarantees.

    I agree with this. :)


    Cheers,

    - Alf
     
    Alf P. Steinbach, May 13, 2009
    #12
  13. magnus.moraberg

    Jerry Coffin Guest

    Right -- AFAIK, there's nothing that says the implementation shall
    create an object of type std::ios_base::Init.

    [ ... ]
    Yes, I think that's a reasonable summary.
     
    Jerry Coffin, May 13, 2009
    #13
  14. magnus.moraberg

    James Kanze Guest

     
    James Kanze, May 14, 2009
    #14
  15. magnus.moraberg

    James Kanze Guest

    [...]
    On rereading, I think you're right. The formal guarantee is
    only present if there is at least one instance of ios_base::Init
    is destructed. I'm pretty sure that this is a defect, however.

    In practice, every implementation I know defines a static
    instance of ios_base::Init in <iostream>. So any program which
    includes <iostream> will construct an instance of this object.
    This was required by the ULS specification of <iostream.h>, but
    (Actually, I don't think that it was the intent that <iostream>
    suffice for using cout, etc. But since that's what all
    implementations do, people have come to expect it.)

    Yes. C++0x also guarantees that if <iostream> is included, the
    standard objects will be constructed before any variables at
    namespace scope which follow the include. The usual way of
    achieving this is to declare a static instance of ios_base::Init
    I don't know what the actual intentions were, but I do know what
    implementations do today, and what users expect. Practically
    speaking, any implementation which doesn't do what is expected
    will be considered wrong by the users, regardless of what it
    can argue from the standard.

    (My proposal concerning <iostream>, for example, wasn't based on
    what the original intentions were, nor on what I thought they
    should be, but on what implementations actually do and users
    actually expect.)
     
    James Kanze, May 14, 2009
    #15
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.