Sign in to follow this  

Finally in CPP

This topic is 4089 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I was told there is somethin called finally in CPP that can be implemented with the try mechanisem. What I want to do is that a given scope (i.e. {}) of code, will be completly executed even if there is an exception in the middle of it. In case an exception occured while in this block, the program will finish doing the whole block and then it will trhow the exception onward. A simple example would be:
delete this->ptr;
this->ptr = NULL;
The problem with this code is that if an exception occur in the middle of delete this->ptr; it wont reach the line which sets this->ptr to NULL. This is the sort of problem I want to solve. I can simply do the following:
T * ptrKeep;
ptrKeep = this->ptr;
this->ptr = NULL;
delete ptrKeep;
But I want to do it with the try{} mechanisem. Thanks in advance.

Share this post


Link to post
Share on other sites
In the special case of a pointer, you can use auto_ptr. (Which, BTW, it tells you right on that same page right below the question about finally.)

Share this post


Link to post
Share on other sites
Quote:
The problem with this code is that if an exception occur in the middle of delete this->ptr; it wont reach the line which sets this->ptr to NULL.

...

The only time that should raise an exception (AFAIK) is when the this pointer is invalid. In which case your object doesn't exist or is otherwise fuxxored, and you've probably got more problems than a pointer not being set to NULL.

Share this post


Link to post
Share on other sites
You will also want to look into the RAII idiom. It is essential for implementing exception-safe code in a sane, manageable fashion (which is what you seem to really want to know). Google can help you out there, as well. std::auto_ptr and boost::shared_ptr are very helpful for this as well.

Also, your example is flawed. A destructor should never throw an exception. Read this for more information.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mushu
Quote:
The problem with this code is that if an exception occur in the middle of delete this->ptr; it wont reach the line which sets this->ptr to NULL.

...

The only time that should raise an exception (AFAIK) is when the this pointer is invalid. In which case your object doesn't exist or is otherwise fuxxored, and you've probably got more problems than a pointer not being set to NULL.


or if the destructor of whatever ptr is throws an exception, in which case he's still got more problems then an object not being set to NULL :)

Share this post


Link to post
Share on other sites
Lets just repeat that for emphasis : Write all destructors as if they have an exception specification of throw(). Furthermore, assume all destructors behave as if they have an exception specification of throw(). A destructor that throws is always exception un-safe. You gain nothing from attempting to correct that behavior in client code.

However, delete CAN throw. In the case where delete throws, the destructor has already been called. Now, delete should only throw when you have corrupted the heap somehow, or aren't deleting a deletable object. All these cases are usually caught by the operating system as an access violation or seg fault, and represent a much more sinister bug.

When delete is called in a destructor, and that destructor is called as a result of stack unwinding while propogating an exception, and that call to delete throws an exception, there is nothing you can do anyway. When delete exits by throwing, while stack unwinding is already occuring, the result is a call to terminate.

In general, when you error while releasing a resource, theres not much you can do. Should you try and release it again? Probably not. It will just fail again. Usually, the best you can do is forget about it.

But there is certainly a benefit when the resource in question is something such as a socket or file handle. Failing to close a file handle is an error; but you should still try and clean up any other handles you have open.

Incidentally, the solution to your problem is just to not propogate the exception. If you fail to release the memory, theres nothing more you can do about that exception, so just forget it. Your code becomes


try
{
delete pointer;
} catch (...) {}
pointer = 0;


If delete throws, the memory is leaked. But it was going to leak anyway.

Share this post


Link to post
Share on other sites
Quote:
Original post by Deyja
However, delete CAN throw. In the case where delete throws, the destructor has already been called. Now, delete should only throw when you have corrupted the heap somehow, or aren't deleting a deletable object. All these cases are usually caught by the operating system as an access violation or seg fault, and represent a much more sinister bug.


Just to clarify, delete cannot throw a C++ exception. It may throw an exception of the underlying hardware/operating system (a Windows Structured Exception under Windows), but there is no guarantee that this exception can be caught by the C++ exception mechanism (under Windows only C++ exception mechanisms which are built on top of the Windows Structured Exception mechanism can catch Windows Structured Exceptions. I believe only Visual C++ is implemented this way due to legal reasons).

Σnigma

Share this post


Link to post
Share on other sites
If delete throws it doesnt necessarly means the memory is leaked.
Somethng could have happen in the dtr of that memory, such as a file error or something like that.
After setting the pointer to NULL, why not propegate the exception?
The thing is, the most important thing I want when an exception error happen in my program, is to catch where it happend. So I can solve this bug.

Share this post


Link to post
Share on other sites
Quote:
Original post by Enigma
Quote:
Original post by Deyja
However, delete CAN throw. In the case where delete throws, the destructor has already been called. Now, delete should only throw when you have corrupted the heap somehow, or aren't deleting a deletable object. All these cases are usually caught by the operating system as an access violation or seg fault, and represent a much more sinister bug.


Just to clarify, delete cannot throw a C++ exception. It may throw an exception of the underlying hardware/operating system (a Windows Structured Exception under Windows), but there is no guarantee that this exception can be caught by the C++ exception mechanism (under Windows only C++ exception mechanisms which are built on top of the Windows Structured Exception mechanism can catch Windows Structured Exceptions. I believe only Visual C++ is implemented this way due to legal reasons).

Σnigma

It can, if the dtr explictly throws a C++ excpetion.

Share this post


Link to post
Share on other sites
Quote:
Original post by Julian90
then in the catch block use a macro to log it to a file along with __FILE__ and __LINE__

i.e.

try
{
delete pointer;
}
catch (...)
{
LOG(........);
}

That is not the problem.
The problem is that after an exception has occured, trying to continue running your program is a suicide.


Share this post


Link to post
Share on other sites
Quote:
Original post by The C modest god
If delete throws it doesnt necessarly means the memory is leaked.
Somethng could have happen in the dtr of that memory, such as a file error or something like that.


Exceptions trigger stack unwinding. Stack unwinding automatically causes destructors to be run (which was the answer to your original question). Raising an exception while one is already pending will systematically terminate the program. Therefore your destructors must not throw, nor rely on operations that may throw. You need the "nothrow guarantee"*.

Quote:
The thing is, the most important thing I want when an exception error happen in my program, is to catch where it happend. So I can solve this bug.


When an exception is raised, you should catch it at the point where you can do something about it. That may very well not be "where it happened", which is precisely why exceptions propagate on their own.

Quote:
The problem is that after an exception has occured, trying to continue running your program is a suicide.


Just because you were handed a std::bad_alloc or std::out_of_range doesn't mean that your program is doomed. It just means you have to take corrective action.


* There are three level of exception safety guarantees you can provide for an operation. You always need to at least provide the basic guarantee: if an exception is raised during the operation, the program is left in a consistent, if unpredictable state. The class invariants are preserved, etc. With the strong guarantee, an operation must either succeed or have no effect. If an exception is thrown mid-way, the program state must be left unchanged. Finally providing the nothrow guarantee means that the operation just cannot fail.
Both destructors and swap operations (std::swap, std::vector::swap ...) both must provide the nothrow guarantee.

Share this post


Link to post
Share on other sites
Quote:
If delete throws it doesnt necessarly means the memory is leaked.
Somethng could have happen in the dtr of that memory, such as a file error or something like that.

No, the destructor is called before the system reclaims memory. If it throws, the memory leaks.

Quote:

Just to clarify, delete cannot throw a C++ exception. It may throw an exception of the underlying hardware/operating system (a Windows Structured Exception under Windows), but there is no guarantee that this exception can be caught by the C++ exception mechanism (under Windows only C++ exception mechanisms which are built on top of the Windows Structured Exception mechanism can catch Windows Structured Exceptions. I believe only Visual C++ is implemented this way due to legal reasons).

That's what I get for using VC++ then, I guess. But there's no reason the library implementation couldn't throw C++ exceptions. Even if the hardware communicates failure using smoke signals and navy flags, the implementation of delete could translate them into exceptions.
Still pointless. Whatever kind of exception you get from delete, you can't do anything usefull besides ignore it.

Share this post


Link to post
Share on other sites
in delphi's rtl's code, there's a FreeAndNil procedure, translate it to c++:


template<typename T>
inline void FreeAndNil(T*& v)
{
T* ptr=v;
v=NULL;
delete ptr;
}


haha, everything's done :)

or use destructor to simulate finally to take the same effect:

template<typename T>
struct doomer
{
T*& ptr;
doomer(T*& p):ptr(p)
{
};
~doomer()
{
ptr=NULL;
}

};

Share this post


Link to post
Share on other sites
Lets say I recognize in one of the dtr that something is wrong. So I save the position of this wrong thing.
Now I want to stop my program and report it to the user.
I cant do it inside the dtr.
Is there a way to have a function operate immediatly after all the dtr have finished?

Share this post


Link to post
Share on other sites
Quote:
Lets say I recognize in one of the dtr that something is wrong. So I save the position of this wrong thing.
I think you may have the wrong idea about exceptions. Exceptions are not what functions do when they "recognize that something is wrong". Exceptions are thrown to indicate that an operation was not performed, and (sometimes) to explain why.
Quote:
Now I want to stop my program and report it to the user.
I cant do it inside the dtr.

Why not?

Share this post


Link to post
Share on other sites
Quote:

Lets say I recognize in one of the dtr that something is wrong. So I save the position of this wrong thing.
Now I want to stop my program and report it to the user.
I cant do it inside the dtr.
Is there a way to have a function operate immediatly after all the dtr have finished?


If a failure occurs in a destructor, there is very rarely any sane corrective action you can take -- what are you going to try and do if you can't close a file? Try again? And again and again and again? Log the failure and move on. You can do this in the destructor since it doesn't mean throwing.

You do not need to terminate the program in the face of an exception. Exceptions don't mean, "Oh crap, time to quit," they mean that something has failed and the operation cannot be completed.

The catch() handler is the "function" that operates after "all the dtr" of the objects whose lifetime began within the try block have been executed as a result of an exception.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
Quote:

Lets say I recognize in one of the dtr that something is wrong. So I save the position of this wrong thing.
Now I want to stop my program and report it to the user.
I cant do it inside the dtr.
Is there a way to have a function operate immediatly after all the dtr have finished?


If a failure occurs in a destructor, there is very rarely any sane corrective action you can take -- what are you going to try and do if you can't close a file? Try again? And again and again and again? Log the failure and move on. You can do this in the destructor since it doesn't mean throwing.

You do not need to terminate the program in the face of an exception. Exceptions don't mean, "Oh crap, time to quit," they mean that something has failed and the operation cannot be completed.

The catch() handler is the "function" that operates after "all the dtr" of the objects whose lifetime began within the try block have been executed as a result of an exception.

Of course that is what the catch do, but they have told me I should never throw an exception from a dtr, so the catch should never be reached.

Share this post


Link to post
Share on other sites
No.
Where you throw has nothing to do with where you catch. You should not throw from a destructor, but that doesn't mean you can't catch in one, if you (for some reason) needed to.

This is what I mean:

try
{
foo bar; // does not throw
foo baz; // does not throw

// do some operations that throw
}
catch(std::exception &e)
{
// bar and baz have both had their destructors executed by the time we get here
}

Share this post


Link to post
Share on other sites

This topic is 4089 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this