I've been studying C++ for two months and now I'm studying the chapter in my book (Programming Principles and Practice using C++) about errors. But after studying the first pages I have a question: what is the difference between a runtime error and a logic error? According to my book a runtime error is an error found by checks at runtime and we can further classify runtime errors as:
hardware error / operating system error
errors detected by a library
errors detected by user code (what is user code?)
while logic errors are simply errors found by the programmer looking for the causes of erroneous results.
I thought I had understood this difference but then an example provided by the author created doubts in me. This is the example:
#include "std_lib_facilities.h"
int area(int lenght, int width) // calculate area of rectangle
{
return lenght * width;
}
int framed_area(int x, int y) // calculate area within frame
{
return area(x - 2, y - 2);
}
int main()
{
int x = -1;
int y = 2;
int z = 4;
int area1 = area(x, y);
int area2 = framed_area(1, z);
int area3 = framed_area(y, z);
double ratio = double(area1) / area3;
}
Here is what the author briefly says about this example :
the calls of the function area() and framed_area() lead to negative
values, representing areas, being assigned to area1 and area2. Should
we accept such erroneous results? But before answering these question
look at the calculation of ratio, in the calculation of ratio area3
will be 0 and the division with 0 will lead to hardware-detected error
that terminates the program with some cryptic message. This is the
kind of error that you, or your user, will have to deal with if you
don't detect and deal sensibly with runtime errors.
What I don't understand here is why a negative value used as an argument for a function that calculates the area is treated as a runtime error, isn't it just a logic error? I thought that runtime errors were only errors due for example by dividing a number by 0 and other special cases. Am I wrong or am I just misunderstanding something? What is the real difference between logic errors and runtime errors? Could you show me some little examples?
A runtime error may legitimately happen: e.g. some file containing garbage data, or some wrong human input, or some lack of resource (no more memory, disk space full, broken hardware, network connection failure).
A logic error (or a failing assert ....) is by definition always the symptom of some bug in the program, e.g. a supposedly sorted array used for dichotomical access which happens to be unsorted.
See the documentation of <stdexcept> header:
std::logic_error: This class defines the type of objects thrown as exceptions to report errors in the internal logical of the program, such as violation of logical preconditions or class invariants.
std::runtime_error: This class defines the type of objects thrown as exceptions to report errors that can only be detected during runtime.
I believe that internal logical for std::logic_error is a typo, I understand it as internal logic (of the program), but I am not a native English speaker.
If you formalize the specification of your program (e.g. with the help of Frama C using ACSL), you might find (and perhaps correct) logic errors; but you should care about runtime errors. But you could also have bugs in specification.
Read about Ariane 5 flight 501 failure. And look into J.Pitrat's blog to get some other views.
Your book is certainly right in the first sub-division, compile-time vs. run-time.
If you can, make sure you get the former instead of the latter, as it's automatically detected.
It's also right that both can be further sub-divided, but let us only look at the latter:
External error: hardware-error / operating-system error
Many of those are expected, like the network being unreliable, a user-file missing, and the like. Often, one can recover from them.
Others are unexpected, and often unrecoverable, like a missing required dependency, unreliable RAM, running out the clock, and the like. Expect to simply crash-and-burn.
Input error: Mangled input-data, missing input-data, whatever.
This should be detected by your application, which depending on severity of the error might substitute defaults, try to recover it or just crash-and-burn.
Logic error: The fundamental assumptions of the program have been found wanting.
Nothing can be depended on to hold any longer, the only sane option is to crash-and-burn immediately to try and contain the damage.
Common logic-errors are fencepost-errors (off-by-one), race-conditions, use-after-free and the like.
Be aware that std::logic_error, despite its name, need not always signify a fatal breakdown in the programs logic, and thus might be expected and relatively benign.
With regards to the example you provided, providing negative input to the method calculating area is a logical error because you already (as the developer of the application) know that this is wrong. But assume that you wrote the method to calculate the area and you gave this to a user who does not know what calculating area is about - he may provide input which is incorrect and this would lead to incorrect results. In this case, it would be a runtime error as the user is unaware and there is no error checking in place.
Logic errors are due to flaws in the thinking of the programmer and are preventable. For example, forgetting to safeguard against out of bounds errors in an array.
Runtime errors, on the other hand, arise from computer architecture and operating system and are beyond the control of the programmer and cannot be predicted easily during coding.
#include <iostream>
using std::cout;
using namespace std;
int area(int lenght, int width) // calculate area of rectangle
{
return lenght * width;
}
int framed_area(int x, int y) // calculate area within frame
{
return area(x - 2, y - 2);
}
int main()
{
int x = -1;
int y = 2;
int z = 4;
int area1 = area(x, y);
int area2 = framed_area(1, z);
int area3 = framed_area(y, z);
double ratio = double(area1) / area3;
cout<<area1<<endl
<<area2<<endl
<<area3<<endl
<<ratio<<endl;
system("pause");
return 0;
}
How about trying this there is no runtime error,I don't know how you can see the output result without cout .
I am a new leaner too , I think it is important to practice in short periods of time when studying .
This is just my suggestion as two answers above has well explained .
Runtime error is happening when your computer system(operating system and / or firmware) has to handle unpredicted instructions. Most of the time, your program is crashed when a runtime error is occurred. For example, a memory leak which is an allocation of new memory that is never deallocated. However, logic error can be easily recognized when you have an unpredicted behavior in your program. Let's say you expect your program to add two numbers but instead it multiplies them. This is a type of logic error that can be fixed just by using the right operator.
Hopefully this helps
From my experience with myself :) …
The confusion may stem from our “making life simple” by re-using std exception logic when we are supposed to build one for ourselves. The std exceptions are to be caught (not thrown) by our code. They inform us what has happened when calling a function from the std library and the logic of the exception inheritance tree is not simply transferable outside the library.
std::runtime_error tells us Huh! Something unpredictable has happed
std::logic_error tells us Ohoh! You omitted something important in your code
Your boss can write a program like
try { mainCall(); }
catch (const std::logic_error &) { std::cout << "No premiums this year!"; }
catch(const std::runtime_error &) { std::cout << "I know it’s not your fault..."; }
Now the problem arises when we want to re-use this logic in our code. The exceptions are there, ready for use, with the std::string constructors and the .what() method, so why to reinvent the wheel, right?
Now the dilemma comes:
double a,b;
std::cin >> a;
std::cin >> b;
if (b == 0) EXCEPTION;
else std::cout << a/b;
So what is the EXCEPTION like? Is it a runtime_error or a logic_error?
Did something unpredictable happen? Could we have expected that a user enters 0 for b? Yes we could and we , indeed, did! So it must not be the runtime_error.
Did we omitted something important in our code? No, we did not! So it must not be the logic_error either.
We intuitively tend to assign runtime_errors to hardware errors and logic_errors to “logical” errors – thus one would opt for logic_error in the example above. But intuition is often a bad guide in programing... just supply your code into your boss’ test unit ;)
Conclusion:
Derive your own exception tree starting from the std::exception (or whatever) or do not try to solve the runtime/logic_error dilemma. It does not have a solution outside the std library domain…
Related
I want my program to throw an error when a defined maximum number of objects of a certain class (MAX) is reached. I must limit the number of instantiations because I'm working with a framework that provides a limited amount of resources.
Currently I have the following (simplified):
class Resource {
private:
static int count;
public:
Resource();
};
int Resource::count = 0;
Resource::Resource() {
if (++count > MAX) {
throw std::domain_error("Cannot create more resources.");
}
}
Is the use of std::domain_error recommended or should I use another type? Or should I not throw an error at all and follow a different approach?
I will answer the general question, without considering any of the details of the application.
Is the use of std::domain_error recommended or should I use another
type? Or should I not throw an error at all and follow a different
approach?
Regarding which exception to throw: std::domain_error specifically has to do with the argument of a function being outside the allowed range, so that is not a good fit. As #NathanOliver suggests, std::runtime_error would be a good option. Or a custom exception derived from that.
Regarding throwing an exception or using a different approach: An important question is when the error is expected to happen and who can handle it.
Exceptions are typically used for errors that can happen during normal operation, and preferably be handled at run-time.
If the amount of resources allocated is determined during development, and excessive allocation of resources indicates a bug, then an assertion is an option:
Resource::Resource() {
assert(++count <= MAX);
}
That will make a debug-build of the program crash (fail-fast) if the limit is exceeded, which is often quite useful during development, testing, and debugging but should obviously not be used if the limit can be exceeded in the finished product. (As assertions are typically removed in release builds it would go unnoticed and cause whatever problems or undefined behaviour exceeding the limit would cause).
Exceptions vs assert has been asked here before: Design by contract using assertions or exceptions?, Assertion VS Runtime exception, C++ error-codes vs ASSERTS vs Exceptions choices choices :(, Design by contract using assertions or exceptions?, etc. (*) There are also books, like Herb Sutter's Coding Standards that talk about this. The general consensus seems to be this:
Use assertions for internal errors, in the sense that the user of the module and the developer are one and the same person/team. Use exceptions for everything else. (**)
This rule makes a lot of sense to me, except for one thing. I am a scientist, using C++ for scientific simulations. In my particular context, this means that I am the sole user of most of my code. If I apply this rule, it means I never have to use exceptions? I guess not, for example, there are still I/O errors, or memory allocation issues, where exceptions are still necessary. But apart from those interactions of my program with the "outside world", are there other scenarios where I should be using exceptions?
In my experience, many good programming practices have been very useful to me, in spite of those practices being designed mostly for large complex systems or for large teams, while my programs are mostly small scientific simulations which are written mostly by me alone. Hence this question. What good practices of exception use apply in my context? Or should I use only asserts (and exceptions for I/O, memory allocation, and other interactions with the "outside world")?
(*) I hope that after reading the complete question, you agree that this is not a duplicate. The topic of exceptions vs assert has been dealt with before in general, but, as I try to explain here, I don't feel that any of those questions addresses my particular situation.
(**) I wrote this with my own words, trying to resume what I've read. Feel free to criticize this statement if you feel it does not reflect the majority's consensus.
assert() is a safeguard against programmer mistakes, while exceptions are safeguards against the rest of existence.
Let's explain this with an example:
double divide(double a, double b) {
return a / b;
}
The obvious problem of this function is that if b == 0, you'll get an error.
Now, let's assume this function is called with arguments which values are decided by you and only you. You can detect the problem by changing the function into this:
double divide(double a, double b) {
ASSERT(b != 0);
return a / b;
}
If you have accidentally made a mistake in your code so that b can take a 0 value, you're covered, and can fix the calling code, either by testing explicitely for 0, or to make sure such a condition never occurs in the first place.
As long as this assertion is in place, you will get some level of protection as the developer of the code.
It is a contract that makes it easy to see what kind of problem can occur in the function, especially while you are testing your code.
Now, what happens if you have no control over the values that are passed to the function ?
The assertion will just disrupt the flow of the program without any protection whatsoever.
The sensible thing to do is this:
double divide(double a, double b) {
ASSERT(b != 0);
if (b == 0)
throw DivideByZeroException();
return a / b;
}
try {
result = divide(num, user_val);
} catch (DivideByZeroException & e) {
display_informative_message_to_user(e);
}
Note that the assertion is still in place because it is the most readable indication of what can go wrong.
The addition of the exception, however, allows you to recover more easily from the problem.
It can be argued that such an approach is redundant, but in a release build, the assertions will usually be NOOPs without generated code, so the exception remains the sole protection.
Also, this function is very simple, so the assertion and the exception throw are immediately visible, but with a few dozens of lines of code added, that would not be the case anymore.
Now, when you are developing and likely to do mistakes, the assertion failure will be visible at exactly the line where it occured, while the exception might bubble up into an unrelated try/catch block that would make it harder to pintpoint the problem exactly, especially if the catch block does not log stack traces.
So, if you want to be safe and mitigate the risks of mistakes during development and during normal execution, you can never be too careful, and might want to provide both mechanisms in a complementary way.
I'm in a similar situation; engineering software, sole developer, very few users of my programs. My rule of thumb is to use exceptions when the program could feasibly recover from an error, or when a user should be expected to react to the error in some way. An example is checking for negative numbers where only positive numbers are allowed: the program doesn't need to terminate because the user typed in a negative value for mass, they just need to recheck their inputs and try again.
On the other hand, I use asserts to catch major bugs in the software. In the event that some error occurs from which the program has no hope of recovering (or that the user has no hope of fixing themselves), I just let the assert print out the file name and line number so that the user can report it to me, and I can fix it. An example of where I would use an assert is checking that the number of rows and columns of a matrix are equal when I'm expecting a square matrix. If num_rows != num_cols, then something is seriously broken with the code and some debugging is required. In my opinion, this is easier than trying to imagine all the possible ways that a matrix could become invalid, and test for them all.
As far as performance, I only disable or remove asserts and other error checks in critical sections of code, and then only when that section has been thoroughly tested and debugged.
My approach is probably not good for production software though. I can't imagine some program like Microsoft Excel bombing out with an "assertion failed" message. Ha ha. It's one thing if the three coworkers who use your software complain about your error-handling strategy, but quite another if you have thousands of unhappy customers who paid cash for it.
I'd use assertions where I expect the check to have a performance impact. I.e. when writing a vector or matrix class of simple types (e.g. double, complex<double>), and I wanted to make a bounds check I'd use assert(), because the check there has a potentially large performance impact, since it happens with every element access. I can then turn off this check in production builds with -DNDEBUG.
If the cost of the check does not matter (e.g. a check that an initial solution does not contain NaN values before you pass it to an iterative scheme), I would use an exception or another mechanism that is also active in production builds. If your job aborts after waiting in the queue of a cluster for three days and running for 10 hours, you at least want to have a diagnostic better than "killed (SIGSEGV)", so you can avoid rebuilding in debug-mode, waiting another 3 days and spending another 10 hours of expensive computation time.
The are situations where neither exceptions nor asserts are appropriate. An example would be an error where the cost of checking does not matter, but that is nevertheless fatal enough that the program cannot continue under any circumstances. An assertion is not appropriate, because it only triggers in debug mode, an exception is not appropriate, because it can (accidentally) be caught, obscuring the problem. In such a case I'd use a custom assert macro, that does not depend on NDEBUG, e.g.:
// This assert macro does not depend on the value of NDEBUG
#define assert_always(expr) \
do \
{ \
if(!(expr)) \
{ \
std::cerr << __FILE__ << ":" << __LINE__ << ": assert_always(" \
<< #expr << ") failed" << std::endl; \
std::abort(); \
} \
} while(false)
(This example was taken from here with a modified name to indicate the slightly broader purpose).
If I have a simple class such as...
class Rectangle
{
private:
double length;
double width;
public:
void setLength(double l) { length = l; }
void setWidth(double w) { width = l; }
void getLength() { return length; }
void getWidth() { return width; }
void getArea() { return length * width; }
};
...and the person using it calls the mutator setWidth() with an invalid argument, say -1.0, what is the correct way to handle this? When I say correct, should I for example, change the return type from void to bool or possibly an int and return a value to indicate whether or not the operation occurred successfully, or am I to allow the value to be set since theoretically it will not break anything, but any values returned as a result, say from getArea() will be garbage and the user will have to debug what he/she did wrong?
Sorry if this is a silly question, but two text books later, and I'm still not quite sure about how classes I write should be implemented for others to use.
You have a couple choices. In the real world, it will depend on who is using your class (other internal code or public API) as well as conventions agreed upon by your company.
You can:
Throw Exception
Pros:
Simple to implement
Keeps error handling code clean
Allows passing of error messages and other variables
Standard for most exceptional situations
Harder to ignore than a boolean return
Can be thrown from constructor
Cons:
Can be overused in areas where normal conditional statements should be used (this situation doesn't count)
You are introducing more "invisible" breaking points into an application. Many times users of a class don't know what exactly is going to be thrown because it either isn't documented or he/she didn't read the documentation.
Assert
Pros:
Rule of thumb: if you are going to crash, crash early
Good for situations where you as the developer call the code and have control over the input
Strong debugger support (typically)
Cons:
Gets excluded from non-debug builds so not good for times when user can still input bad data
Boolean Returns
Pros:
Quick/Easy to implement
Smaller footprint than exception handling (although this is negligible these days)
Cons:
Can't pass back any extra information to the caller (why did it break?)
Breaks down when you are trying to be consistent through your entire project and a simple boolean won't cut it
Easier to ignore compared to exceptions
Evaluate Data Type
Don't be afraid to go back and check if the data type of your property makes sense for what you are doing. In this case, it does. However, I can think of times where developers have used standard integers when they really wanted unsigned integers or strings when they really needed a single character.
You should throw an exception:
#include <stdexcept>
// ...
void setLength(double len)
{
if (len < 0)
throw new std::invalid_argument("len");
length = len;
}
Depends on the usage of the class.
If you want a user friendly message to be passed on to caller, you can put an if condition and throw an exception (e.g. std::invalid_argument) with a good description. Update the documentation and signature appropriately of course.
If you expect this should never happen in real life, but can happen in, say testing phase of the development, then I recommend using assert. As in production/release build the asserts are "removed" from code by compiler. So while testing (debug build, e.g. -DNDEBUG and/or -g option for gcc) if someone makes a mistake, you'll see a clear assertion failure, but in real/production environment no error would be reported and the extra condition won't harm the performance.
Usually when compiler is invoked with -DNDEBUG option, it's
Some references for assert and throw syntax.
Either return false; or throw something like invalid_value.
How an error should be handled is completely requirement/project dependent. If your project require to continue with wrong user input (which could be very dangerous in real life software) that's what you need to do. Similarly, if the project needs protection against wrong user input, that must be addressed.
When you design a class to be used by other user, you must make sure it can't be used any wrong way. You must fool proof your code. If any bug emerges from the software where your class will be used, you should write the class in such a way so that the bug doesn't come out from your code.
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?
"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).
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::runtime_error("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.what() << "!\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.
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.
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.
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.
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:
It clutters up the code with error handling code to the point that the actual logic is obfuscated.
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?)
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:
Exceptions are usually very costly when used. They have to be eschewed, despite their advantages, if performance is paramount.
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.
"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.
Because of their often high cost of use, carelessly using them for all error handling can make your code slow and bloated.
I've programmed C and C++ for a long time and so far I've never used exceptions and try / catch. What are the benefits of using that instead of just having functions return error codes?
Possibly an obvious point - a developer can ignore (or not be aware of) your return status and go on blissfully unaware that something failed.
An exception needs to be acknowledged in some way - it can't be silently ignored without actively putting something in place to do so.
The advantage of exceptions are two fold:
They can't be ignored. You must deal with them at some level, or they will terminate your program. With error code, you must explicitly check for them, or they are lost.
They can be ignored. If an error can't be dealt with at one level, it will automatically bubble up to the next level, where it can be. Error codes must be explicitly passed up until they reach the level where it can be dealt with.
The advantage is that you don't have to check the error code after each potentially failing call. In order for this to work though, you need to combine it with RAII classes so that everything gets automatically cleaned up as the stack unwinds.
With error messages:
int DoSomeThings()
{
int error = 0;
HandleA hA;
error = CreateAObject(&ha);
if (error)
goto cleanUpFailedA;
HandleB hB;
error = CreateBObjectWithA(hA, &hB);
if (error)
goto cleanUpFailedB;
HandleC hC;
error = CreateCObjectWithA(hB, &hC);
if (error)
goto cleanUpFailedC;
...
cleanUpFailedC:
DeleteCObject(hC);
cleanUpFailedB:
DeleteBObject(hB);
cleanUpFailedA:
DeleteAObject(hA);
return error;
}
With Exceptions and RAII
void DoSomeThings()
{
RAIIHandleA hA = CreateAObject();
RAIIHandleB hB = CreateBObjectWithA(hA);
RAIIHandleC hC = CreateCObjectWithB(hB);
...
}
struct RAIIHandleA
{
HandleA Handle;
RAIIHandleA(HandleA handle) : Handle(handle) {}
~RAIIHandleA() { DeleteAObject(Handle); }
}
...
On first glance, the RAII/Exceptions version seems longer, until you realize that the cleanup code needs to be written only once (and there are ways to simplify that). But the second version of DoSomeThings is much clearer and maintainable.
DO NOT try and use exceptions in C++ without the RAII idiom, as you will leak resources and memory. All your cleanup needs to be done in destructors of stack-allocated objects.
I realize there are other ways to do the error code handling, but they all end up looking somewhat the same. If you drop the gotos, you end up repeating clean up code.
One point for error codes, is that they make it obvious where things can fail, and how they can fail. In the above code, you write it with the assumption that things are not going to fail (but if they do, you'll be protected by the RAII wrappers). But you end up paying less heed to where things can go wrong.
Exception handling is useful because it makes it easy to separate the error handling code from the code written to handle the function of the program. This makes reading and writing the code easier.
return an error code when an error condition is expected in some cases
throw an exception when an error condition is not expected in any cases
in the former case the caller of the function must check the error code for the expected failure; in the latter case the exception can be handled by any caller up the stack (or the default handler) as is appropriate
Aside from the other things that were mentioned, you can't return an error code from a constructor. Destructors either, but you should avoid throwing an exception from a destructor too.
I wrote a blog entry about this (Exceptions make for Elegant Code), which was subsequently published in Overload. I actually wrote this in response to something Joel said on the StackOverflow podcast!
Anyway, I strongly believe that exceptions are preferable to error codes in most circumstances. I find it really painful to use functions that return error codes: you have to check the error code after each call, which can disrupt the flow of the calling code. It also means you can't use overloaded operators as there is no way to signal the error.
The pain of checking error codes means that people often neglect to do so, thus rendering them completely pointless: at least you have to explicitly ignore exceptions with a catch statement.
The use of destructors in C++ and disposers in .NET to ensure that resources are correctly freed in the presence of exceptions can also greatly simplify code. In order to get the same level of protection with error codes you either need lots of if statements, lots of duplicated cleanup code, or goto calls to a common block of cleanup at the end of a function. None of these options are pleasant.
Here's a good explanation of EAFP ("Easier to Ask for Forgiveness than Permission."), which I think applies here even if it's a Python page in Wikipedia. Using exceptions leads to a more natural style of coding, IMO -- and in the opinion of many others, too.
When I used to teach C++, our standard explanation was that they allowed you to avoid tangling sunny-day and rainy-day scenarios. In other words, you could write a function as if everything would work ok, and catch the exception in the end.
Without exceptions, you would have to get a return value from each call and ensure that it is still legitimate.
A related benefit, of course, is that you don't "waste" your return value on exceptions (and thus allow methods that should be void to be void), and can also return errors from constructors and destructors.
Google's C++ Style Guide has a great, thorough analysis of the pros and cons of exception use in C++ code. It also indicates some of the larger questions you should be asking; i.e. do I intend to distribute my code to others (who may have difficulty integrating with an exception-enabled code base)?
Sometimes you really have to use an exception in order to flag an exceptional case. For example, if something goes wrong in a constructor and you find it makes sense to notify the caller about this then you have no choice but to throw an exception.
Another example: Sometimes there is no value your function can return to denote an error; any value the function may return denotes success.
int divide(int a, int b)
{
if( b == 0 )
// then what? no integer can be used for an error flag!
else
return a / b;
}
The fact that you have to acknowledge exceptions is correct but this can also be implemented using error structs.
You could create a base error class that checks in its dtor whether a certain method ( e.g. IsOk ) has been called. If not, you could log something and then exit, or throw an exception, or raise an assert, etc...
Just calling the IsOk on the error object without reacting to it, would then be the equivalent of writing catch( ... ) {}
Both statement would display the same lack of programmer good will.
The transport of the error code up to the correct level is a greater concern. You would basically have to make almost all methods return an error code for the sole reason of propagation.
But then again, a function or method should always be annotated with the exceptions it can generate. So basically you have to same problem, without an interface to support it.
As #Martin pointed out throwing exceptions forces the programmer to handle the error. For example, not checking return codes is one of the biggest sources of security holes in C programs. Exceptions make sure that you handle the error (hopefully) and provide some kind of recover path for your program. And if you choose to ignore an exception rather than introduce a security hole your program crashes.