views:

365

answers:

9

One is to use C++ exceptions: try catch blocks. But freeing dynamic memory will be an issue when an exception is raised.

Second is to use C style: errno variable

Third is just to return -1 on error and 0 on success :)

Which way should be chosen for a mid-size project and why? Any other better approach..?

+27  A: 

But freeing dynamic memory will be an issue when an exception is raised.

No it's not. std::vector<int> v(100); Done.

The concept here is called Scope-Bound Resource Management (SBRM), also known by the much more common (and awkward) name Resource Acquisition Is Initialization (RAII). Basically, all resources are contained in some object which will clean up the resource in the destructor (which is always guaranteed to be run for an automatically allocated object). So whether or not the function exists normally or via exception, the destructor is run and your resource is cleaned up.

Never do an allocation where you need to free it explicitly, use containers and smart pointers.

GMan
Agreed in general, although I would avoid using "never" in a guideline like this -- for every rule, there are exceptions. Experience is about learning where those exceptions make sense.
Stabledog
@Stable: "for every rule, there are exceptions" is that a rule? In any case, besides writing the resource container itself, you will *never* have to not use the container. It never makes sense to put yourself in a position to have to be very careful about remembering to delete something, it can always be automated in a destructor.
GMan
There are very few exceptions if you are using C++ when it comes to RAII. All resources allocated in a class should be freed by the time the class instance is destroyed. All memory allocations and deallocations should occur within classes with the exception of memory allocators which should still be used to allocate and deallocate memory within a class. For exception safety, RAII should not be considered an option; it's mandatory.
@GMan: Do you know where the name SBRM comes from? Did you make that up? (I'm giving a lecture on RAII and while I agree that SBRM is a much better name, I can't seem to find where it comes from). Thanks.
James McNellis
@James: Hm, I first saw the name mentioned by Johannes on some answer on the site (I think [this one](http://stackoverflow.com/questions/395123/raii-and-smart-pointers-in-c/395519#395519)), but I have no idea where he saw it (I would ask him on that question, I'm curious to know.). I *think* I've also seen it in a GotW article, but Googling is failing me so maybe not. If I had to guess it was made by someone in `comp.lang.c++`, `comp.std.c++`, or `##c++` as a response to disliking/intending to improve the name RAII. Would be interesting to chase down the source.
GMan
@Stabledog: so? I'd rather have a simple rule like "never explicitly free memory" and then deal with the exceptions myself when they arise, than a much more ambiguous and vague rule that tries to take every exception into account.
jalf
People understand rules like "never do X". A rule like "don't do X unless you really want to, and think you have a good reason" just becomes an open invitation to do X as much as you like.
jalf
I like RAII, I'm not arguing against it. I'm arguing against absolutism and religion in programming. But I suppose that should be saved for a different thread. :)
Stabledog
+3  A: 

Second is to use C style: errno variable

Third is just to return -1 on error and 0 on success :)

And how do they help solving your problem of freeing dynamic memory? They also use an early-exit strategy, same as throw.

So in summary, they don’t have an advantage over C++ exceptions (according to you).

Konrad Rudolph
How does changing which value gets returned mandate early-exit? `int func() { int returnValue = 0; ... (may set returnValue to other values); return returnValue; }`
Bill
@Bill: well that’s using the single point of exit convention that’s pretty much out of vogue, and for good reason. It doesn’t actually make the control flow any better. And it can also be applied with C++ exceptions, of course. No difference, really. Just an extra variable necessary to cache the exception to throw.
Konrad Rudolph
+1  A: 

Throw an exception. Destructors of variables are always called when an exception is thrown, and if your stack-based variables don't clean up after themselves (if for example you used a raw pointer when you need to delete the result), then you get what you deserve. Use smart pointers, no memory leaks.

DeadMG
+2  A: 

Have a look at this comment by Herb Sutter on try catch for C++ GOTW. And do go through his whole set of articles. He does have a lot to say on when and how to check and save yourself from error conditions and how to handle them in the best ways possible.

DumbCoder
+3  A: 

In the first place, you should strive for a program with minimum error cases. (Because errors are not cool.)

Exceptions are a nice tool but should be used conservatively: reserve them for "exceptional cases", do not use them to control the flow of your program.

For example, do not use exceptions to test whether a user input is correct or not. (For such a case, return an error code.)

Julien L.
+1  A: 

But freeing dynamic memory will be an issue when an exception is raised.

Freeing memory (or any other resource for that matter) doesn't suddenly become a non-issue because you don't use exceptions. The techniques that make dealing with these problems while exceptions can be thrown easy, also make it easier when there can be "error conditions".

Noah Roberts
A: 

C++ Exceptions: Pros and Cons.

Nemanja Trifunovic
At least provide a summary of the site you are linking to.
Georg Fritzsche
A: 

Exceptions are good for passing control from one context to another.
You let the compiler do the work of unrolling the stack between the contexts then in the new context compensate for the exception (and then hopefully continue).

If your error happens and can be corrected in the same context then error codes are a good method to do error handling and clean up (Don't take this to mean you should not be using RAII you still need that). But for example within a class an error occurs in a function and the calling function can correct for that type of error (then it probably is not an exceptional circumstance so no exceptions) then error code are useful.

You should not use error codes when you have to pass information out of a library or sub system as you are then relying on the developer using the code to actually check and handle the code to make sure it works correctly and more often than not they will ignore error codes.

Martin York
+2  A: 

One is to use C++ exceptions: try catch blocks. But freeing dynamic memory will be an issue when an exception is raised.

@see RAII.

Exceptions should be your preferred method of dealing with exceptional runtime situations like running out of memory. Note that something like std::map::find doesn't throw (and it shouldn't) because it's not necessarily an error or particularly exceptional case to search for a key that doesn't exist: the function can inform the client whether or not the key exists. It's not like a violation of a pre-condition or post-condition like requiring a file to exist for a program to operate correctly and finding that the file isn't there.

The beauty of exception-handling, if you do it correctly (again, @see RAII), is that it avoids the need to litter error-handling code throughout your system.

Let's consider a case where function A calls function B which calls C then D and so on, all the way up to 'Z'. Z is the only function that can throw, and A is the only one interested in recovering from an error (A is the entry point for a high-level operation, e.g., like loading an image). If you stick to RAII which will be helpful for more than exception-handling, then you only need to put a line of code in Z to throw an exception and a little try/catch block in A to catch the exception and, say, display an error message to the user.

Unfortunately a lot of people don't adhere to RAII as strictly as they should in practice, so a lot of real world code has more try/catch blocks than should be necessary to deal with manual resource cleanup (which shouldn't have to be manual). Nevertheless, this is the ideal you should strive to achieve in your code, and it's more practical if it's a mid-sized project. Likewise, in real world scenarios, people often ignore error codes returned by functions. if you're going to put the extra mile in favor of robustness, you might as well start with RAII because that will help your application regardless of whether you use exception handling or error code handling.

There is a caveat: you should not throw exceptions across module boundaries. If you do, you should consider a hybrid between error codes (as in returning error codes, not using a global error status like errno) and exceptions.

It is worth noting that if you use operator new in your code without specifying nothrow everywhere, ex:

int* p = new int(123); // can throw std::bad_alloc
int* p = new(std::nothrow) int(123); // returns a null pointer on failure

... then you already need to catch and handle bad_alloc exceptions in your code for it to be robust against out of memory exceptions.