Detect if my destructor is called by unwinding through an exception?

D

Douglas Peterson

I have a simple class that verifies some data between the beginning and the
end of a function:

class Verify
{
Verify() { note the data value }
~Verify() { check if data value is the same as we noted }
};

void foo()
{
Verify _v;
...
throw SomeException; // test can fail because _v is destroyed
'prematurely'
...
};

~Verify() just makes an assertion to warn me that I've done something wrong
in the function body.
If the exception occurs, the verification is no longer relevant.
Is there some way to detect that I'm (~Verify()) being destroyed by an
exception rather than the end of scope?
i.e.
~Verify()
{ if (!exception) assert(...); }

I know I could add another variable into the function, or set some member of
Verify before I throw, but that defeats the point of using the Verify class
as a simple way to test the integrity of the function.
 
C

codigo

Douglas Peterson said:
I have a simple class that verifies some data between the beginning and the
end of a function:

Why not keep data constant in the function instead? Or implement a verify()
method in your data class otherwise?
class Verify
{
Verify() { note the data value }
~Verify() { check if data value is the same as we noted }
};

void foo()
{
Verify _v;
...
throw SomeException; // test can fail because _v is destroyed
'prematurely'
...
};

~Verify() just makes an assertion to warn me that I've done something wrong
in the function body.
If the exception occurs, the verification is no longer relevant.
Is there some way to detect that I'm (~Verify()) being destroyed by an
exception rather than the end of scope?

To verify that Verify zapped your destructor? You should rethink the design
here. A constructor should be performing construction, not verification.
Ditto for the destructor. Throwing exceptions from a ctor or d~tor is a good
example of a_big_no_no.
i.e.
~Verify()
{ if (!exception) assert(...); }

I know I could add another variable into the function, or set some member of
Verify before I throw, but that defeats the point of using the Verify class
as a simple way to test the integrity of the function.

You aren't testing the integrity of the function, you are testing the data's
integrity.
 
D

Douglas Peterson

It's just a assertion to verify that my stack (not the program stack) is
left in the same state it was in when the function began as a sanity check
for when/if the code gets modified.

class Verify
{
public:
Verify() { m_stackDepth = getStackDepth(); }
~Verify() { assert(m_stackDepth == getStackDepth(); }
int m_stackDepth;
};

That's the whole of it. I can put one line at the beginning of every
function that manipulates the stack:

void foo()
{
Verify _verify;
...
}

Simple as pie.

Obviously I could write:

void foo()
{
int _stackDepth = getStackDepth();
...
assert(_stackDepth == getStackDepth());
}

That might seem to solve the problem (since the assertion would be skipped
by a throw), but many of the roughly 60 functions that use this have
alternate return paths. The best solution IMO was to toss the check into a
class object.

If a throw occurs during execution of the function, the stack depth (again
keep in mind we're not talking about the program stack) being out of sync is
no longer relevant, the entire stack gets destroyed as a result of the error
condition.

I can just leave it as is of course, but it's annoying getting the assertion
when it has no meaning.
 
A

abecedarian

Douglas Peterson said:
If the exception occurs, the verification is no longer relevant.
Is there some way to detect that I'm (~Verify()) being destroyed by an
exception rather than the end of scope?
i.e.
~Verify()
{ if (!exception) assert(...); }

Try uncaught_exception() (though this function is deemed to be
unelegant).

A.C.
 
S

Serge Skorokhodov (216716244)

Douglas Peterson wrote:
If the exception occurs, the verification is no longer
relevant. Is there some way to detect that I'm (~Verify())
being destroyed by an exception rather than the end of scope?

std::uncaught_exception() (from header <exception>) returns true
if an unhandled exception exists. I guess this is the case you're
interested in.

Borland C++Builder 6 and MS VS.NET 2003 do contain this function.
 
P

Peter Koch Larsen

Douglas Peterson said:
I have a simple class that verifies some data between the beginning and the
end of a function:

class Verify
{
Verify() { note the data value }
~Verify() { check if data value is the same as we noted }
};

void foo()
{
Verify _v;
...
throw SomeException; // test can fail because _v is destroyed
'prematurely'
...
};

~Verify() just makes an assertion to warn me that I've done something
wrong in the function body.
If the exception occurs, the verification is no longer relevant.
Is there some way to detect that I'm (~Verify()) being destroyed by an
exception rather than the end of scope?
i.e.
~Verify()
{ if (!exception) assert(...); }

I know I could add another variable into the function, or set some member
of Verify before I throw, but that defeats the point of using the Verify
class as a simple way to test the integrity of the function.

You can't. Some advise you to use uncaught_exception, but this will not work
in the general case as your function might be called as a result of
unwinding an exception. (Meaning you will not get your check done in that
case). Still, I do not understand why you do not care about consistent
objects after an exception. If you allow bad objects in case of exceptions,
then you risk having them all the time.

/Peter
 
H

Howard

Douglas Peterson said:
It's just a assertion to verify that my stack (not the program stack) is
left in the same state it was in when the function began as a sanity check
for when/if the code gets modified.

class Verify
{
public:
Verify() { m_stackDepth = getStackDepth(); }
~Verify() { assert(m_stackDepth == getStackDepth(); }
int m_stackDepth;
};

That's the whole of it. I can put one line at the beginning of every
function that manipulates the stack:

void foo()
{
Verify _verify;
...
}

Simple as pie.

Obviously I could write:

void foo()
{
int _stackDepth = getStackDepth();
...
assert(_stackDepth == getStackDepth());
}

That might seem to solve the problem (since the assertion would be skipped
by a throw), but many of the roughly 60 functions that use this have
alternate return paths. The best solution IMO was to toss the check into a
class object.

Instead of inserting an object inside your functions, how about calling your
functions inside a wrapper function? If your function signatures are the
same (or at least if there are a limited number of them), then this should
work. Something like this:

StackCheckVoid( foovoid() );
StackCheckVoid( barvoid() );
int x = StackCheckInt( fooint() ); // where foo int returns an int

Or, even a template to do something similar?

-Howard
 
D

Douglas Peterson

That's a very good solution--far too obvious for an idiot such as I - doh!

I wish I had thought of it before I started.

They are all callback functions that have the exact same signiture, though I
don't see how that might help. There is no way to forward the 'real' address
through a check function. I'll have to create a wrapper for each and change
every reference in the source.

Thanks Howard.
 
R

Richard Herring

In message said:
Douglas Peterson said:
It's just a assertion to verify that my stack (not the program stack) is
left in the same state it was in when the function began as a sanity check
for when/if the code gets modified.
[...]


Instead of inserting an object inside your functions, how about calling your
functions inside a wrapper function? If your function signatures are the
same (or at least if there are a limited number of them), then this should
work. Something like this:

StackCheckVoid( foovoid() );

Ahem. I don't think you want to call foovoid() just yet :-(

Try this: StackCheckVoid(foovoid);
 
H

Howard

Richard Herring said:
In message said:
Douglas Peterson said:
It's just a assertion to verify that my stack (not the program stack) is
left in the same state it was in when the function began as a sanity
check
for when/if the code gets modified.
[...]


Instead of inserting an object inside your functions, how about calling
your
functions inside a wrapper function? If your function signatures are the
same (or at least if there are a limited number of them), then this should
work. Something like this:

StackCheckVoid( foovoid() );

Ahem. I don't think you want to call foovoid() just yet :-(

Try this: StackCheckVoid(foovoid);

D'oh! Quite correct, that's what I meant.
Thanks,
-Howard
 
H

Howard

That's a very good solution--far too obvious for an idiot such as I - doh!

I wish I had thought of it before I started.

They are all callback functions that have the exact same signiture, though
I don't see how that might help. There is no way to forward the 'real'
address through a check function. I'll have to create a wrapper for each
and change every reference in the source.

As Richard pointed out, I made a mistake there. There shouldn't be
parentheses on the function parameters in my example. But there shouldn't
be a problem passing a function as a parameter. It's easiest if you make a
typedef (alias) for the function type, then use that new type as the
StackCheck function's parameter type. Look up function pointers in your
favorite book.

-Howard
 
D

Douglas Peterson

They are callback functions of another library.

SomeLibraryRegisterForCallback1(foo);

int foo(somelibrarytype * ptr)
{ // gets called by the library in response to some event
}

There is no way to have foo (in the example above) be a generic StackCheck
because there is no way to forward the address of foo through that call.
Although I have access to the object being passed in, I have no way of
knowing which event is being fired except by registering different function
addresses for different events (without changing the library source of
course). So I have to wrap every one of them:

SomeLibraryRegisterForCallback1(foocheck);
int foocheck(somelibrarytype * ptr)
{
Verify _v;
return foo(ptr);
}

Anyhow, it's done and I'm happy with it. A bit of macro'ing even removes it
all for release builds.

Thanks again for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,479
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top