It looks from my debug statements within the program, the
smart pointer destructor is never called and therefore,
the object pointed never deleted.
Here, is the program.
#include "stdafx.h"
#include <stdio.h>
class CAddress
{
public:
CAddress(){ printf("Debug : Address Object
Created.\n");}
virtual ~CAddress(){printf("Debug : Address Object
destroyed.\n");}
};
template<class T>
class CSmartPtr
{
public:
CSmartPtr(T* ptr = NULL);
virtual ~CSmartPtr();
private:
T* m_ptr;
};
template<class T>
CSmartPtr<T>::CSmartPtr(T* ptr):m_ptr(ptr)
{
printf("Debug : SmartPtr Constructor called\n");
}
template<class T>
CSmartPtr<T>::~CSmartPtr()
{
printf("Debug : CSmartPtr Distructor called\n");
delete m_ptr;
}
int main(int argc, char* argv[])
{
printf("Debug : Main function called.\n");
{
int i, j;
i = 1;
try{
CSmartPtr<CAddress> smtObj(new
CAddress());
j = 10/(i-1); // This is intended
to break the program
}catch(...)
{
printf("Debug : exception block
entered.\n");
}
printf("Debug : After End of exception
block.\n");
}// I expect the smart pointer destructor called
here.
printf("Debug : End of Main Function\n");
return 0;
}
I expected debug output like this
Debug : Main function called.
Debug : Address Object Created.
Debug : SmartPtr Constructor called
Debug : CSmartPtr Distructor called
Debug : Address Object destroyed.
Debug : After End of exception block.
Debug : End of Main Function
Press any key to continue
But the output is
Debug : Main function called.
Debug : Address Object Created.
Debug : SmartPtr Constructor called
Debug : exception block entered.
Debug : After End of exception block.
Debug : End of Main Function
Press any key to continue
It seams the try{}catch() statement is not doing anything
other than prevent program crash. I expected after
preventing the program crash, control to return to the
program and destructor of the smart pointer called after
the block it is declared.
Thanks
1. It's not good practice to rely on catch(...) to catch hardware exceptions
such as division by zero. This is a dangerous feature that will be removed
in the next version of VC.
2. If you're expecting destructors to run, make sure you're compiling with
/EHa and NOT with /GX or /EHs.
When compiled with /EHa, your code behaves as you expect with VC6+SP5 and
VC7.1 (didn't try VC7).
-cd
Read about Structured Exception Handling (SEH), __try, __except and
__finally
--
With best wishes,
Igor Tandetnik
"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken
>
> Read about Structured Exception Handling (SEH), __try, __except and
> __finally
> --
> With best wishes,
> Igor Tandetnik
>
> "For every complex problem, there is a solution that is simple, neat,
> and wrong." H.L. Mencken
>
My documentation says this is "Microsoft Specific." Is this correct?
If so is there a portable alternative?
-Kanon
Yes it is.
>
> If so is there a portable alternative?
No, there is not.
-cd
_set_se_translator will convert hardware exceptions to C++ exceptions.
--
Craig Powers
MVP - Visual C++
Generally, the kinds of things that cause operating system exceptions
are undefined behavior (e.g. almost all access violations). Nothing
that deals with undefined behavior will be portable.
please, tell me where i can find explanation of this position.
Much has been written. I don't have time to look up some references at the
moment, but I'll bookmark this message and if noone else has responded by
this evening, I'll try to find a good discussion of the subject.
If you're up for some google work, try looking for postings by myself, Doug
Harrison or William De Pallo in this newsgroup within the past year.
-cd
Andy:
<disclaimer>
Unlike the majority of my VC++ pals here, I
a) don't own a copy of the C++ standard
b) have never read it
c) am not on the leading edge in terms of my usage of the language
So, take what I say with a grain of salt and keep looking for Doug's and
Carl's posts on the topic.
</disclaimer>
The problem as I see it in mixing and matching the operating system's
structured exceptions (SEs) and C++ exceptions is that it makes it too easy
to mask serious errors in an application. Very serious errors like division
by zero, floating point overflow, access violations, etc are the stuff of
SEs. They are always almost a result of a programming error. And by the time
one is signaled the waters may be poisoned to such an extent that graceful
recovery is impossible.
C++ exceptions are almost always due to more benign causes. For example, a
file I/O class might throw an exception on encountering an end of file.
That's no biggie. But if you use this catch clause "catch (...)" then its
hard to separate the wheat from the chaff.
Nevertheless, SEs do occur all the time in development. And in development
it is very important to locate the bug, find the guilty developer and hound
him until he fixes the problem. To that end, and only to that end, I wrote a
class that translates SEs into C++ exceptions. It can do little more than
report the location and cause of the fault. I wrote it so that I could point
a "developer" at the output and say "fix your damned bug" in the gentlest of
tones. <BSEG>
With it, you can write a catch clause like
catch (Win32Exception e)
{
std::cout << e.toString();
DebugBreak();
}
which may help to diagnose the problem.
If you think it will help you, and if google hasn't turned it up yet, I'll
post it again.
Regards,
Will
Well, it appears you posted it as an attachement, and attachments
aren't showed by Google. I think it would interest a lot of people
(including myself) if you posted it again.
Arnaud
MVP - VC
> Well, it appears you posted it as an attachement, and attachments
> aren't showed by Google. I think it would interest a lot of people
> (including myself) if you posted it again.
D'accord. I've placed the sample inline, below.
Note that for this two work you will need to build with the /EHa option. (If
I recall correctly the 7.x IDE will warn you about that while 6.0 does
not).
That's because the optimizing compiler would see no way in which a C++
exception might be thrown below and will remove the try/catch machinery,
failing to realize that an exception can come from an environment which it
knows nothing about.
Regards,
Will
P.S. I expect word wrap will make a mess of the comment banners that precede
the member functions. :-(
--------------------------------------------------------------------------
Here is a quick hack that forces a division by zero:
--------------------------------------------------------------------------
#include <windows.h>
#include <math.h>
#include <iostream>
#include "Win32Exception.h"
int main(int argc, char **argv)
{
int nZero, nQuotient;
double fZero;
// Enables SEH to C++ exception translation
Win32Exception::enable();
try
{
fZero = sin(0.00);
nZero = (int) fZero;
nQuotient = 1 / nZero;
}
catch (Win32Exception e)
{
std::cout << e.toString();
std::cout << std::endl;
}
// Disables SEH translation
Win32Exception::disable();
return 0;
}
------------------------------------------------------------------------
Here is the header for the Win32Exception class:
------------------------------------------------------------------------
#include <eh.h>
#include <string>
class Win32Exception
{
private:
DWORD dwExcCode;
DWORD dwAddress;
static int tlsIndex;
static char fmt[];
static void sehTranslator(UINT u, LPEXCEPTION_POINTERS pexp);
public:
Win32Exception(DWORD, LPEXCEPTION_POINTERS);
Win32Exception(DWORD, DWORD);
~Win32Exception();
bool static enable(int);
bool static enable(void);
void static disable(void);
DWORD getAddress() const;
DWORD getCode() const;
std::string getType() const;
std::string getModuleName() const;
DWORD getModuleOffset() const;
std::string toString() const;
};
------------------------------------------------------------------------
Here is the source for the Win32Exception class:
------------------------------------------------------------------------
/*************************************************************************
This class enables C++ code to catch Win32 structured exceptions. Its
static member function handle() can be used to specify whether or not
structured exceptions should be handled on a per-thread basis. Where
they are handled a Win32Exception is thrown. The class includes a
toString() function which details the exception. Several other member
functions exist to determine the exception code, the exception type,
the exception address, the name of the module containing the exception
address and the offset from the start of that module to the exception
address.
*************************************************************************/
#include <windows.h>
#include "Win32Exception.h"
/*************************************************************************
For each thread we save the address of the previous SE handler
translator. We make no attempt to maintain a chain of previous handlers.
We assume that we want to translate structured exceptions to C++
exceptions or we want the default behavior.
*************************************************************************/
int Win32Exception::tlsIndex = -1;
/*************************************************************************
Old habits die hard deaths
*************************************************************************/
char Win32Exception::fmt[] = "%08.08X";
/*************************************************************************
The constructor saves the exception code and the address where the
structured exception was encountered.
*************************************************************************/
Win32Exception::Win32Exception(DWORD dwCode, LPEXCEPTION_POINTERS pexp)
{
dwExcCode = dwCode;
dwAddress =
reinterpret_cast<DWORD>(pexp->ExceptionRecord->ExceptionAddress);
}
/*************************************************************************
The constructor saves the exception code and the address where the
structured exception was encountered.
*************************************************************************/
Win32Exception::Win32Exception(DWORD dwCode, DWORD dwLocation)
{
dwExcCode = dwCode;
dwAddress = dwLocation;
}
/*************************************************************************
The destructor doesn't do anything
*************************************************************************/
Win32Exception::~Win32Exception(){}
/*************************************************************************
This is the exception handler translator. We create an instance of the
Win32Exception class and then throw it.
*************************************************************************/
void Win32Exception::sehTranslator(UINT u, LPEXCEPTION_POINTERS pexp)
{
Win32Exception exc(u, pexp);
throw exc;
}
/*************************************************************************
The function enables the translation of structured exceptions to C++
exceptions.
*************************************************************************/
bool Win32Exception::enable(int nIndex)
{
bool ok;
_se_translator_function oldTrans;
if ( tlsIndex == -1 )
tlsIndex = nIndex;
if ( tlsIndex == -1 )
ok = false;
else
{
oldTrans = _set_se_translator(&Win32Exception::sehTranslator);
TlsSetValue(tlsIndex, oldTrans);
ok = true;
}
return ok;
}
/*************************************************************************
The function enables the translation of structured exceptions to C++
exceptions.
*************************************************************************/
bool Win32Exception::enable(void)
{
if ( tlsIndex == -1 )
tlsIndex = TlsAlloc();
return Win32Exception::enable(tlsIndex);
}
/*************************************************************************
The function disables the translation of structured exceptions to C++
exceptions.
*************************************************************************/
void Win32Exception::disable(void)
{
_se_translator_function oldTrans;
if ( tlsIndex != -1 )
{
oldTrans = (_se_translator_function) TlsGetValue(tlsIndex);
_set_se_translator(oldTrans);
TlsSetValue(tlsIndex, 0);
}
return;
}
/*************************************************************************
The function returns the address where the exception occurred.
*************************************************************************/
DWORD Win32Exception::getCode() const
{
return dwExcCode;
}
/*************************************************************************
The function returns the address where the exception occurred.
*************************************************************************/
DWORD Win32Exception::getAddress() const
{
return dwAddress;
}
/*************************************************************************
The function returns the type of exception that occurred.
*************************************************************************/
std::string Win32Exception::getType() const
{
std::string str;
switch ( dwExcCode )
{
case EXCEPTION_ACCESS_VIOLATION:
str = "Access Violation";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
str = "Datatype Misalignment";
break;
case EXCEPTION_BREAKPOINT:
str = "Breakpoint";
break;
case EXCEPTION_SINGLE_STEP:
str = "Single Step";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
str = "Array Bounds Exceeded";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
str = "Float Denormal Operand";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
str = "Floating Point Divide by Zero";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
str = "Floating Point Inexact Result";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
str = "Floating Point Invalid Operation";
break;
case EXCEPTION_FLT_OVERFLOW:
str = "Floating Point Overflow";
break;
case EXCEPTION_FLT_STACK_CHECK:
str = "Floating Point Stack Check";
break;
case EXCEPTION_FLT_UNDERFLOW:
str = "Floating Point Underflow";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
str = "Integer Division by Zero";
break;
case EXCEPTION_INT_OVERFLOW:
str = "Integer Overflow";
break;
case EXCEPTION_PRIV_INSTRUCTION:
str = "Privileged Instruction";
break;
case EXCEPTION_IN_PAGE_ERROR:
str = "In Page Error";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
str = "Illegal Instruction";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
str = "Non-continuable Exception";
break;
case EXCEPTION_STACK_OVERFLOW:
str = "Stack Overflow";
break;
case EXCEPTION_INVALID_DISPOSITION:
str = "Invalid Disposition";
break;
case EXCEPTION_GUARD_PAGE:
str = "Guard Page";
break;
case EXCEPTION_INVALID_HANDLE:
str = "Invalid Handle";
break;
default:
str = "Unknown";
break;
}
return str;
}
/*************************************************************************
This function translates the exception to a displayable string.
*************************************************************************/
std::string Win32Exception::toString() const
{
char szAddress[10];
std::string str;
str = "An exception of type 0x";
wsprintf(szAddress, fmt, dwExcCode);
str.append(szAddress);
str.append( " - '");
str.append( getType() );
str.append( "' - occurred at 0x");
wsprintf(szAddress, fmt, dwAddress );
str.append(szAddress);
str.append( " in module '");
str.append( getModuleName() );
str.append( "' at offset 0x");
wsprintf(szAddress, fmt, getModuleOffset() );
str.append(szAddress);
return str;
}
/*************************************************************************
This function returns the name of the file that contains the module
where the exception occurred.
*************************************************************************/
std::string Win32Exception::getModuleName() const
{
int n;
char szModule[_MAX_PATH + 1];
std::string str;
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
VirtualQuery(reinterpret_cast<const void *>(dwAddress), &mbi, sizeof(mbi));
n = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase),
szModule, _MAX_PATH);
if ( n > 0 )
str = szModule;
else
str = "Unknown?";
return str;
}
/*************************************************************************
This function returns the offset from the start of the module to
the instruction where the exception occurred.
*************************************************************************/
DWORD Win32Exception::getModuleOffset() const
{
DWORD dwOffset;
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
VirtualQuery( reinterpret_cast<const void *>(dwAddress), &mbi,
sizeof(mbi));
dwOffset = dwAddress - reinterpret_cast<DWORD>(mbi.AllocationBase);
return dwOffset;
}
the 'void*' is my best guess as VirtualQuery
expects 'void*'.
I will appriate if you repost only the lines containing
the reinterpret_cast with '<' and '>' replaces something
like ' ot ".
Thanks
>.
>
>-----Original Message-----
>"Jama" <jc6...@yahoo.com> wrote in message
>news:081501c35514$db350de0$a501...@phx.gbl...
>> Will, thank you very much for posting your code.
>
>You are welcome.
>
>> I have copied it into a VC++ project but some
information has
>> been lost as a result of copying.
>
>Problem with word wrapping?
>
>I have attached a ZIP containing two source files and a
header. Don't forget
>to build your project with /EHa
>
>Regards,
>Will
>
>
>