Classes and error handling

This is a discussion on Classes and error handling within the C++ Programming forums, part of the General Programming Boards category; I thought I'd try writing a class that handles some (random access) file operations for me. Since there are tons ...

  1. #1
    The larch
    Join Date
    May 2006
    Posts
    3,573

    Classes and error handling

    I thought I'd try writing a class that handles some (random access) file operations for me. Since there are tons of errors that might happen with files, I also thought I'd try to use C++ exception handling to go with the class.

    I read a tutorial on exceptions but the whole thing remained somewhat unclear (well, I'll need to reread it or look for some other tutorial...)

    The main question is: should this class even have exception handling, or may-be it should be up to the user of the class to do that?

    If it should, would this class include another header with the exception class?

    Also, how would you handle possible failure of the constructor (to open the file in question)? Or should I even worry about that as a designer of the class (with std::fstream it's up to the user to check if constructor was successful)?

  2. #2
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,796
    >should this class even have exception handling
    I don't see why not.

    >or may-be it should be up to the user of the class to do that?
    Some would say this is a step backward because to notify the user you need to provide an interface that returns error codes. That can complicate the interface when you can simply specify what exceptions are thrown by the class.

    >would this class include another header with the exception class?
    That's a style issue, and it depends on what exceptions you throw. If you derive a new exception class from one of the standard classes, it's up to you how you organize it. If you just use one of the general exception classes, you need to include <stdexcept> and/or <new>.

    >how would you handle possible failure of the constructor (to open the file in question)?
    If you choose to treat this as a critical error that stops construction, you would throw an exception.

    >Or should I even worry about that as a designer of the class
    It's up to you.
    My best code is written with the delete key.

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    7,340
    Opening a file is a tricky case. If the filename comes from some end-user, then it might not be uncommon for the file to fail to open. That isn't exactly an exceptional circumstance, and you generally want to save your exceptions for exceptional situations. On the other hand, it is an important feature of the class to be able to open a file with the constructor, and you can't return a failure code from the constructor. That means leaving it up to the class user to check a status flag if you don't use exceptions, which isn't ideal.

    The standard file streams use the status flag method, but I think an exception would be appropriate as well. You should definitely "worry" about which option to pick, but I agree with Prelude that in the end it is up to you. Also, try to remain consistent throughout your interface. For example, if you are using exceptions for most major errors, then it might make sense to use exceptions in the constructor as well for consistencies sake.

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Opening a file is a tricky case. If the filename comes from some end-user, then it might not be uncommon for the file to fail to open. That isn't exactly an exceptional circumstance, and you generally want to save your exceptions for exceptional situations.
    Well, I foresee 2 kinds of errors: failing to open a file, and trying to read/write beyond end of file (e.g data-piece #120 in a file that has 100 items of data). Both of these errors would be caused either by class-user or end-user. If correct parameters are provided the class shouldn't fail. So you are saying that exceptions are rather meant for cases where writing to file fails for example because the computer has caught fire?

    What remained unclear to me is where the line goes between the responsibilities of class writer and class user.

    I've read that there are basically two ways to handle thrown exceptions: terminate and recover (usually by retrying). The two file errors mentioned above certainly do not call for termination. However, I can't see how the class would handle recovering. Retrying to open a file that can't be opened is meaningless. So it seems up to the class user to figure out what to do with the exceptions? And if he ignores them, then the program would just terminate in a rather clean way?

    Could it be that I have these questions because as mentioned above these errors are not quite exceptions?

    Anyway, thanks for the replies. I'll give the whole thing some more thought and try some other tutorials on it.

    (As a self-taught programmer it is so easy just to ignore concepts that are hard to understand. But the last thing that I'd want to happen - again - is having another nice GUI program crashing under the eyes of our IT fellow )

  5. #5
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,796
    >So you are saying that exceptions are rather meant for cases where writing to
    >file fails for example because the computer has caught fire?
    An exception is just that: a situation that you don't expect to happen. If the file is user provided, failure to open would not be exceptional.

    >there are basically two ways to handle thrown exceptions: terminate and recover
    Terminate is such a strong word. Propagate would be a better choice because you can throw an exception and higher level code will probably be in a better position to recover. Only if no code anywhere can handle the exception will termination occur.

    >Could it be that I have these questions because as mentioned above these errors are not quite exceptions?
    Yes.
    My best code is written with the delete key.

  6. #6
    Cat
    Cat is offline
    Registered User
    Join Date
    May 2003
    Posts
    1,571
    I'd still use exceptions for things like bad user input, or any other file error. As opposed to giving an error code, it forces the caller to handle the error, or let the program crash. I find it also makes much less code for error checking and handling, as you don't need to explicitly check the return of each function, but can call several in the same try...catch (assuming that all of those exceptions should be handled in the same way).

    And yes, if you use exceptions, the class user is forced to choose how to handle them, or allow the program to terminate. I can forsee recovery from a failed file open -- a notice to the user that the file could not be opened, and the option for the user to either specify a different file or end the program (assuming file opening is vital for the program to continue; if not, continue without opening file would be a third option to provide).
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  7. #7
    Hardware Engineer
    Join Date
    Sep 2001
    Posts
    1,398
    I'll give the whole thing some more thought and try some other tutorials on it.
    FYI - Thinking In C++ Volume II (free online!!!) has a full chapter on exceptions.

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,048
    If you're looking for books, try the C++ Book Recommendations thread.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,511
    Quote Originally Posted by Prelude
    An exception is just that: a situation that you don't expect to happen. If the file is user provided, failure to open would not be exceptional.
    I'm not sure if I follow you on this one, Prelude...

    I don't see exceptions the same way. Definitely I don't try to link the word "exception" to the imagery associated with it. An exception is, I believe just a mechanism to separate error reporting from error handling as opposed to more traditional error handling techniques based on error codes or illegal states. More important perhaps is the notion that an exception may not necessarily be an error.

    I do agree that an exception shall be seen from the code POV, not from the coder. But often, a smart way to handle certain errors and other exceptions that are not directly linked to the code is by providing an exception handling mechanism; Just like failing to open a file.

    The alternative would be to use a mechanism that exception handling is thought to replace. That of returning an error value and checking for that value. There's often less code involved too by using exception handling.

    Also (although not entirely on topic), exception handling is sometimes used as a return mechanism with little to no relation to error handling.
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  10. #10
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,796
    >I'm not sure if I follow you on this one, Prelude...
    Exceptions are a bulky and time consuming process in C++. As such, they should be reserved for truly abnormal behavior to avoid the overhead and code bloat. Let's assume that giving the user control over something produces a random correct/incorrect result over a controlled sequence. That said, we can expect about 50% of the files that the user wants to open to have some kind of problem that would cause the open to fail (misspelled name, wrong directory, non-existent file, etc...). A failure condition for half of the requests is not abnormal behavior, so throwing an exception isn't the best option for a user controlled file open.
    My best code is written with the delete key.

  11. #11
    Registered User
    Join Date
    Jan 2005
    Posts
    7,340
    BTW, the book from the book recommendations thread that you should have anyway, C++ Coding Standards, has a section with eight different items about error handling and exceptions. The book is a guide to best practices in C++, so it should help you understand the issues involved even more.

    IMO, based on the information given, there really is no wrong answer. So once you make your decision, just follow through on it and don't second-guess yourself.

  12. #12
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Thinking in C++ vol 2 actually was the book I was reading in the first place
    Now after reading some other tutorials things seem a little clearer. So let's see if I have a plan that could actually work.

    The constructor of the class needs to open a file. It probably needs to allocate a couple of char arrays as well. (If I ever want to use it with wxWidgets for example, std::string wouldn't be a very good choice, because I'd be using wxString in the program?)

    So, presuming that std::fstream constructor doesn't throw exceptions and new does, my constructor would look something like that:

    Code:
    MyFileClass::MyFileClass(char* fileName, someOtherParameters)
    {
        /*never had streams as class variables before
        assuming std::fstream m_file is already constructed here*/
    
        m_file.open(fileName, flags); 
    
        /*since MyFileClass doesn't know what to do about failing to open
        throw an exception (derived CouldNotOpenFile) out of constructor.
        Expecting current object not constructed / destructed*/ 
        
        if (!m_file) throw CouldNotOpenFile(); 
    
        /*Now as for dynamic allocation...*/
    
        try {
            sumthing = new char[somesize];
        }
        catch findOutWhatExactly {
            /*Probably need to terminate here?
            Smells fishy to try allocating again (in a loop) - what if it keeps failing?
            So:
            - log error
            - terminate program here*/
        }
    }
    Is that pseudocode correct? I mean, it's very easy to form misconceptions when learning on one's own.

    (The word Standard scares me off a bit. I'm not learning this to become a professional programmer.)

  13. #13
    Registered User
    Join Date
    Jan 2005
    Posts
    7,340
    The name C++ Coding Standards is a bad name, IMO. It should really be called C++ Best Practices. If you don't want or need to be proficient in C++, then you don't need the book, but otherwise I'd still highly recommend it.

    I'm not familiar with wxWidgets, but I would expect that the C++ string class would work as well with that as C style strings would. And of course C++ strings should certainly be preferred over character arrays in general. You wouldn't have to worry about catching the memory exception either.

    Finally, you could use an initializer list to construct the fstream with the proper filename and flags instead of calling open in the constructor. If you don't use the initializer list, the fstream is default constructed as your comment assumed. Also, if an exception is thrown from the constructor, everything will be cleaned up automatically (assuming you use string instead of a character array).

    So really, all you'd have to do is check for is open and throw the exception if it fails. The other stuff can be moved/removed.

  14. #14
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,511
    Despite the argument of throwing or not an exception or handling the error some other way in case the file fails to open, your throw option is correct. You are giving the caller the responsability to handle the error.

    As for the try block, I hope you don't mean "terminate program here" by means of the terminate function or anything similar. If you want you can log the error and do any cleaning you may feel like... but the proper way to terminate is to rethrow the error afterwards. The caller, missing an error handling routine, will then gracefully terminate deallocating any memory and cleaning the house.
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  15. #15
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Quote Originally Posted by Daved
    I'm not familiar with wxWidgets, but I would expect that the C++ string class would work as well with that as C style strings would. And of course C++ strings should certainly be preferred over character arrays in general. You wouldn't have to worry about catching the memory exception either.
    Well, yes, I think most probably you can construct a wxString of a std::string. And the other way round should be possible too using c_str(). So there really seems no need for char arrays...

    Finally, you could use an initializer list to construct the fstream with the proper filename and flags instead of calling open in the constructor. If you don't use the initializer list, the fstream is default constructed as your comment assumed.
    Dumb me . I was wondering what the syntax would look like. Like this?
    Code:
    MyClass::MyClass(std::string fileName) : m_file(fileName.c_str()) {}
    I have always used plain data types in initializer lists and it hasn't really occurred to me that they all have been constructor calls for that type. Right?

    Also, if an exception is thrown from the constructor, everything will be cleaned up automatically (assuming you use string instead of a character array).
    Do you mean that throw wouldn't cause to exit immediately? So if I throw before allocating, there would still be a memory leak?

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21