views:

710

answers:

5

This question provides more clarity on the problem described here. I did some more investigation and found that the stack unwinding is not happening in the following piece of code:

class One
{
public:
    int x ;
};

class Wrapper
{
public:
    Wrapper(CString csText):mcsText(csText)
    {
     CString csTempText;
     csTempText.Format("Wrapper constructor :: %s\n", mcsText);
     OutputDebugString(csTempText);
    }

    ~Wrapper()
    {
     CString csTempText;
     csTempText.Format("Wrapper destructor :: %s\n", mcsText);
     OutputDebugString(csTempText);
    }
    CString mcsText;
};
class Test
{
    public:

    void notifyError()
    {
        try
     {
      int x = 10; 
     }
     catch(...)  {}
    }

    void OnRecvBuffer()
    {
     try
     {
      Wrapper a("AddRef"); 
      One* p = NULL;
      p->x = 10;
     }
     catch(...)
     {
      notifyError();
     } 
    } 
};



int main() 
{
    Test* pTest = new Test;

    pTest->OnRecvBuffer();

    OutputDebugString("Test");
}

I compiled this code using VC6 SP5 compiler and the output is "Wrapper constructor :: AddRef!!!" (i.e. the destructor of wrapper object which was constructed on stack is not called. Is this the expected behavior ? or is it a bug with VC compiler ? Can I use some compiler flags so that the stack unwinding happens in this case?

+6  A: 

The C++ standard does not give anything to work with in case of Undefined Behavior. Even if MS does. That's a platform specific thing -- so beware. Some such floating point exceptions are turned to Win32 exceptions which you can attempt to catch with _set_se_translator(). The problem is you can catch Win32 exceptions but then your stack will not be unwound properly. At least that is not something you can bet your life on. Wherein lies the futility of the exercise.

Update: The exception is thrown intentionally to check the stack unwinding. The question is why Wrapper class's destructor is not getting called. – Naveen

If this is the case -- don't do it. There are better ways to throw exceptions than via Undefined Behavior.

E.g:

void OnRecvBuffer()
{
    try
    {
        Wrapper a("AddRef");    
        throw 42; // this'll throw an exception without invoking UB
    }
    catch(...)
    {
        notifyError();
    }
}

You cannot dereference a NULL pointer. You are invoking Undefined Behavior here:

One* p = NULL;
p->x = 10;

After that line all bets are off and you could have killed us all ;)

p is a pointer to a One object. It should contain the address of an One object. You have initialized it to 0 -- there is no object at address 0. 0 is not a valid address for any object (this is guranteed by the standard).

dirkgently
The exception is thrown intentionally to check the stack unwinding. The question is why Wrapper class's destructor is not getting called.
Naveen
As I have pointed out that is UB -- what happens when you invoke UB is not defined. No point discussing that. There are other better ways to throw exceptions.
dirkgently
The example program invoked what is undefined behavior for standard C++, but the question is about Microsoft SEH (Structured Exception Handling), not standard C++ exceptions.
bk1e
I can figure that out. But a) SEH is never mentioned in OP's question. b) doing things wrong don't make it a right.
dirkgently
So, stop downvoting my answer since this is perfectly correct.
dirkgently
+2  A: 

Because C++ regular exception does not handle this kind of exception, you have to use SEH which does not know anything about the app and does not unwind.

gbrandt
+4  A: 

If you want to use SEH, you must use _set_se_translator function and /EHa compiler option.

Lazin
A: 

This is undefined behavior:

One* p = NULL;
p->x = 10;

At this point the application is free to crash without unwinding the stack.
If you want to test the stack unwinding replace this with:

 throw 42; // Life the Universe and Everything thrown away

You should not dynamically allocate all your objcts this is C++ not Java!

int main() 
{
    Test    pTest;   // Note the lack of new!

    pTest.OnRecvBuffer();

    OutputDebugString("Test");
}
Martin York
A: 

This is indeed a problem with VC6.

Please refer to the following article for a good explanation: http://www.thunderguy.com/semicolon/2002/08/15/visual-c-exception-handling/

Kim Gybels