views:

211

answers:

6

I am just learning how to handle errors in my C++ code. I wrote this example that looks for a text file called some file, and if its not found will throw an exception.

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
int array[90];
try
{
   ifstream file;
   file.open("somefile.txt");
   if(!file.good())
    throw 56;
}
catch(int e)
{
    cout<<"Error number "<<e<<endl;
}
return 0;
}

Now I have two questions. First I would like to know if I am using Exceptions correctly. Second, (assuming the first is true) what is the benefit to using them vs an If else statement?

+2  A: 

Well, you are using exception correctly in that there is nothing wrong with your code. That said, typically we do not throw primitive types around (even though you can). It is generally a better idea to throw an object that derives from std::exception, and even better to throw an std::exception that is also a boost::exception.

When things are very simple and the handling code and the throwing code are in the same function, then there really is no reason to use exceptions instead of if statements (and, indeed, it would be faster and more efficient to use if...else in that particular case). However, in most situations, the point where the error is discovered and you need to report it is far removed from the logic where the error is to be handled. In many cases the error-recovery logic is specific to the application in question and the logic where the error is discovered cannot make a sensible choice about how to recover from the error, hence the need to throw.

Another benefit of exception handling is that the type of the exception can be used to convey the type of error that occurred. Usually the types in exception hierarchies are much more meaningful than those error codes that end up being used in C code. Also, you cannot ignore an exception as easily as you can ignore an error code; while you can ignore an exception, it will cause the program to die a horrible death. By contrast, if a C function returns an error status code, and you ignore it, it's possible to continue executing and get silently wrong results... in that sense, the use of exceptions is much safer than using error codes.

You may also be interested in reading about exceptions and error handling from the C++ FAQ Lite.

Michael Aaron Safyan
Thanks for the information. Looks like I have more reading to do.
Dr.Ackula
+1 for correct information, -1 for exceptions being "more meaningful" than traditional error codes. Total: 0.
Billy ONeal
@Billy, what are you talking about? If you throw IllegalArgumentException with a message "x must be non-null", that is way more meaningful that simply using EINVAL. And using FileNotFoundException is more helpful than some arbitrary number 22. In the OP's case, it isn't more meaningful, because the OP just threw some random integer, but if you throw a type where the type of the exception tells you exactly what it is, that is way more telling than if you have some random number and you need to figure out which of many macros needs to be used to interpret its value.
Michael Aaron Safyan
Error codes can me mapped to messages just as simply as exceptions can. Win32 does it all the time -- you use the FormatMessage API call ( http://msdn.microsoft.com/en-us/library/ms679351(VS.85).aspx -- use the FORMAT_MESSAGE_FROM_SYSTEM flag)which translates any standard Win32 error code into a human readable message. Regarding IllegalArgumentException vs EINVAL, that's got nothing to do with exceptions; that's just poor naming.
Billy ONeal
@Billy: I think Michael is referring to exceptions' ability to carry additional data along with them, in such a way that the compiler correctly frees the memory even when the `catch` clause is unaware of the extra data.
Ben Voigt
@Ben Voigt: In C++, I can return an error code object which carries around extra data as well. Michael's argument is that good exceptions are better and carry more meaning than bad error codes. That is true. But good error codes carry more meaning than poor exceptions. That does not mean that an exception is inherently more meaningful than an error code. As the OP's code demonstrates, you can throw builtin types just as simply as `std::exceptions`. To be clear, I'm in the pro-exception camp, but stating that an exception is inherently more meaningful is erroneous (yes, pun intended).
Billy ONeal
@Billy, I am not saying that exceptions are automatically more meaningful, I am saying that exceptions, when practiced well, are more meaningful. Even though you can map an integer status code back to a string description of it (e.g. the format message API for win32 or using perror on UNIX), the fact of the matter is that each OS and many libraries each have their own mappings, and so the type system can't really help you out... you have to remember that, oh, this integer here needs to be interpreted using this particular API, and oh, that integer needs to be interpreted using that API, etc.
Michael Aaron Safyan
... with exceptions, the type system ensures that you don't confuse exceptions from different sources or misinterpret their meaning. Also, I should add that on UNIX, all the error codes are nicely given in <errno.h> and the variable errno, but on Windows, you have to use all sorts of different crap... for example, if its socket related then you need to use WSAGetLastError, but if it is something else, then you just use GetLastError.
Michael Aaron Safyan
I agree with Billy on this one. A "meaning" is captured by data just as well as by type. Any meaning you communicate by an exception you can communicate by a variable in an outer scope or an object you return. Sometimes you _can't_ return an error (in constructors for example, or when objects of success occupy the entirety of a function's return space, leaving no space for return codes). But that has nothing to do with one approach offering better communication of a meaning than another. I am not in a strictly "pro-exception camp" but I do think exceptions have their place among the living. :p
wilhelmtell
@Wilhelm, I am not talking about associating information with the exception... I am talking about the compiler giving you type information to ensure that you interpret the exception correctly (according to its type). Whereas an int from one library is easily convertible to an int from another library even if both represent completely different types of error information, you won't be able to assign an exception class from one library to a completely different exception class from another library.
Michael Aaron Safyan
@Michael ok the type-safety is a perfectly valid argument for exceptions, and a good one in fact. Come to think of it, yes, this is an argument about meaning, but that's where I (and Billy?) misunderstood you. I thought you meant conveying meaning for the user, not the compiler.
wilhelmtell
... and when I talked about expressiveness in my answer this is exactly what I meant. When the language splits a feature (`goto`) into multiple weaker features (`if`, `for`, `catch`, ...) then it essentially does so to give the compiler more information about what you're trying to do, so it can give you better error or warning messages.
wilhelmtell
@Billy: That's why I mentioned the part about "compiler correctly frees the memory" (for the associated data) without relying on the caller to be aware of it. Globals can be used in the error code case (but they'd better use thread-local storage) but there's potential to associate the wrong companion information with the error message (WSAGetLastError vs errno vs GetLastError was already mentioned).
Ben Voigt
+3  A: 

First I would like to know if I am using Exceptions correctly.
Yes, though generally you want your exceptions to derive from std::exception.

Second, (assuming the first is true) what is the benefit to using them vs an If else statement?
For the given example, nothing. The benefit of exceptions comes when you have many deep nested functions, like this.

#include <stdexcept>
#include <iostream>
#include <string>

void anErrorFunc(const std::string& x)
{
    ifstream file;
    file.open(x);
    if (!file)
        throw std::exception("Could not open file");
}

void someOtherFunction(const std::string& y)
{
    //Do stuff
    anErrorFunc(y);
    //Do other stuff
}

int main()
{
    try {
        someOtherFunction("somefile.txt");
    } catch (std::exception &ex) {
        std::cout << "Ouch! That hurts, because: "
            << ex.message() << "!\n";
    }
}

Note that the exception will be caught in main(), and someOtherFunction does not have to worry about dealing with passing through failure return codes.

Billy ONeal
+9  A: 

"Correctly" is a value judgment, but (unlike other classes) there's a major benefit from exceptions classes being a monolithic hierarchy, so I'd generally advise throwing something derived from std::exception, not simply an int.

Second, it's open to question whether an incorrect file name is sufficiently unexpected to qualify as a good reason to throw an exception at all.

As to benefits vs. an if/else statement: there are a couple. First, exceptions let you segregate the code that deals with errors, so the main idea and readability of the code don't get lost in a maze of error handling. Second, when you have several layers of code between throwing and catching the exception, the code that throws the exception may not know how it should really be handled. Your code, for example, uses std::cout to report the problem -- but most such code would report errors on std::cerr instead. You can change from one to the other without any change to the code that tried to open the file (which might be deep in a library, and have no clue of which should be used for this application -- and might be used in an application where both are wrong, and MessageBox was preferred).

Jerry Coffin
+1 for the only answer considering what is an "Exceptional" condition.
Billy ONeal
+2  A: 

Syntactically speaking, your code is correct. Idiomatically speaking, maybe not so much -- or at least this depends on the context. When a file can't open then we might do our handling right there inside that if( !file.good() ) if it's perfectly common and possible to happen. For example, if the user asks to open a file in a text editor then it's perfectly plausible and common that the file doesn't exist. On the other hand, if the editor can't find the spelling corpus file then that means something is (arguably) terribly wrong. The program wasn't probably installed, or the user messed around with that file -- anything is possible.

In C++ we use exceptions for exceptional cases. That is, cases that are really not meant to happen and that we don't "accept" to happen. This is as opposed to a user file not opening, or an invalid user input, or no internet connection: these are all examples of perfectly valid things happening, common situations, things we expect to happen sooner or later in a program's run. They aren't exceptional.

Now, what are the benefits of using exceptions compared to conditionals? Allow me to extend this question to any other jump (goto) mechanism: returns as well as conditionals. Exceptions are more expressive if that's what you want to say: if you are dealing with an exceptional case then use exceptions. Exceptions also get more done than plain conditionals, in an analogous way to that of virtual functions getting more done than conditionals. The right code-block will be executed depending on the exception, but also the right scope will handle the exception depending on the handlers.

There are other advantages to exceptions compared with conditionals: exceptions separate error-handling from other code. They allow associating arbitrary data and actions with an error state. They allow communicating success states (via a return) as well as error state (via a throw). And the list goes on...

Technically speaking and at the lowest level exceptions are a sophisticated jump mechanism. Back in the butterfly-days people invented the if conditional as a somewhat-sophisticated goto in order to enhance expressiveness (because a goto can be used for anything really) and to reduce programmer errors. The looping constructs such as the C for loop are also in essence a sophisticated jump with sparkles and rainbow colours, again for reducing errors and enhancing expressiveness. C++ introduced its new casting operators for the same reasons.

So there: exceptions aren't magic, just something somewhat new in the scene compared to conditionals and loops. Don't use them when you don't mean to, just like you don't use a loop when you really mean to use a conditional.

wilhelmtell
I for one would consider a file open failure to be an exceptional condition in certain cases. For example, it's logical for a word processor to assume it can always open a dictionary for it's spellcheck feature because the user shouldn't be mucking around in there.
Billy ONeal
@Billy good point. It's context-based then. I'll update the answer.
wilhelmtell
+1  A: 

Syntactically what you're doing is right. Style-wise, as others have noted, you should be throwing something descended from std::exception.

As to part two of your question, I'd like to go into more detail on that.

The whole point of exceptions is to separate policy from implementation. As Billy ONeal said, you get no benefit at all from using exceptions within the same function that an if statement wouldn't make better. You need to be deeply nested in function calls for it to make sense.

In most code, your high level code has enough information and context to know what to do about errors, but no mechanism to detect them. Your low level code can detect the errors but has none of the information needed to deal with them.

The traditional means of coping with this -- returning error codes -- has a few problems:

  1. It clutters up the code with error handling code to the point that the actual logic is obfuscated.
  2. It relies on programmers not being lazy and checking EVERY error code return, an often foolhardy assumption. (C programmers, be honest here: when was the last time you checked the return value of printf?)
  3. It adds the overhead of error checking and handling to EVERY function call whether there's an error or not.

Exceptions solve these issues (with varying degrees of success).

  • Exceptions solve #1 by only having exception-related code at the point of detection and at the point of handling. Intervening functions don't get cluttered with handling for obscure errors that they themselves have no interest in nor capability of dealing with.
  • They solve #2 by forcing handling. You can't ignore an exception. You have to take action on them. (Lazy programmers can still catch all exceptions and then ignore them, but here their crippling inability to program is now highlighted for all to see.)
  • They solve #3 (when not naively implemented) by having near-zero costs when not used, albeit often at a very, very high cost when actually used.

This is not to say that exceptions are the end-all/be-all of error handling. The disadvantages:

  1. Exceptions are usually very costly when used. They have to be eschewed, despite their advantages, if performance is paramount.
  2. Exceptions lead to very opaque code at times. They are non-local transfers of control -- effectively slightly safer versions of goto statements, but across functions. An exception can transfer control from hundreds of layers deep in your code in source files not even slightly related to the ones you're working on (and, in fact, quite possibly not even accessible to you). This kind of "spooky action at a distance" can make code very difficult to figure out.
  3. "Checked exceptions" can actually be worse for noise generation than the old-style if handling. They tend to be more verbose, you see, than if or switch statements and the fact that you must handle checked exceptions for the code to even compile makes them a liability to many situations.
  4. Because of their often high cost of use, carelessly using them for all error handling can make your code slow and bloated.
JUST MY correct OPINION
+1  A: 

You are not using exceptions correctly. Your code has a much simpler equivalent, without exceptions, which provides the same behaviour.

Exceptions are for when you can't test the results of a function call with other methods. In this case you can, so you shouldn't use exceptions.

As a corollary, you shouldn't throw and catch the exception in the same function body - just do whatever you want to do instead of throwing it.

Daniel Daranas