Head’s up: this is a way geeky question. It’s about C++ calling conventions and exception handling. (Is it bad form for me to use this blog as my own personal help forum?)
I have a strange problem where C++ destructors are not being called for stack-allocated objects when an exception is thrown through my function. For example:
class StackThingy
{
public:
// Constructor will be called when the StackThingy
// is created on the stack.
StackThingy()
{
// (Point A)
}
// Destructor should be called when the StackThingy
// is unwound from the stack.
~StackThingy()
{
// (Point B)
}
};
void __stdcall
MyTestRoutine()
{
// Create the StackThingy
StackThingy thingy;
// Throw an exception. This should unwind the StackThingy
// object, calling its destructor.
throw CException();
}
When I run this code, Point A executes in the constructor, but execution never reaches Point B. It should, because the exception being thrown should unwind stack, calling the destructors on any stack-allocated object. But somehow, the exception would not properly destroy the thingy object.
I narrowed it down to the __stdcall calling convention. If I remove the __stdcall declaration to make it a regular C++ function, then the destructor is called as I would expect. I can find no mention of this behavior in the docs or on the wider web. The description of __stdcall talks about stack maintenance as a key difference between calling conventions, but doesn’t describe exception handling at all.
Is this expected behavior, or is there some other nefarious factor at work here?
Comments
Interesting... Does the throw actually occur? What happens if you wrap your call to MyTestRoutine in a try/catch block? Or add a debugging print statement after the throw?
It'd be shocking if throw turns into a nop in this case, but not much less shocking than the destructor not being called...
I had no luck finding information about this problem either.
BTW, what happens if an __stdcall routine is in the middle of a "normal" call chain, and the lower "normal" function throws? Does unwinding happen correctly across the __stdcall?
Obviously, I should test all these questions myself, but Visual C++ seems to be missing from this Linux box :)
I haven't messed with this in too long but I think __stdcall has the callee cleaning up the stack. If that's the case, could this conflict with the back-end unwinding logic by removing from the frame what it needs to walk up the chain of destructors inside the caller ? If the default calling convention has the caller cleaning up the stack, we can assume something is being removed that shouldn't be.
I'd suggest e-mailing Raymond Chen...C++ exception handling and how it maps to/works with Win32's Structure Exception Handling is a rather complex realm.
It looks like I haven't told the whole story: the code is behaving as I described, but it was due to an ' extern "C" ' declaration on the function.
The throw really occurs, and is caught by a calling function. The odd thing is that destructors are called for the stack objects if the function exits normally, but not if an exception is thrown.
This sounds like a job for Bob. After all, he is one of the few people I know that actually uses "trigraph" in a sentence.
What if you do the decl as StackThingy thingy();
i.e., with the parens? Does that change the code flow at all?
I've never seen a difference with parens or without; it's supposed to be the same either way. Of course, this whole topic is about a C++ compiler dropping the ball on C++ semantics, so all bets are off.
I tried to recreate the problem but I don't see it. Is it possible that there's a calling convention issue between source files? This is just plain weird.
To be more specific, could it be that the function declaration is declared __stdcall and the function definition it is not? Or vice versa? I could envision this problem being caused by the resulting linking issues.
Note that the Microsoft docs don't explicitly say that the definition and declaration both include the __stdcall keyword, but the fact that __stdcall changes the calling conventions makes it seem likely that both are required.
Wow. That's a different story. The behavior of that is undefined. See here : "/GX enables support for synchronous (also called typed or C++) exception handling with the assumption that extern C functions don't throw C++ exceptions. "
Later on....:
"Finally, let's consider whether we can assume that extern "C" functions don't throw C++ exceptions (remember that such an assumption is built into /GX). Extern "C" functions in a C++ program are most likely being linked in from an object module or library. These functions are often written in a different language, such as C or assembler. Because the type of exception we are handling is a feature specifically of the C++ language only, it seems a reasonable assumption to say that an extern "C" function will not throw an exception.
Of course, if an extern "C" function is compiled by the C++ compiler, it is perfectly legal for that function, or one of its called functions, to throw a C++ exception. For those who must throw C++ exceptions from extern "C" functions, replace /GX, which is the same as using /EHsc, with just /EHs. /EHs enables C++ exception handling without the assumption that extern "C" functions will never throw a C++ exception. /EHc enables the assumption that extern "C" functions do not throw exceptions."
Let us know if this helps...
Add a comment: