Re: How to get crash dump when a unhandled CException is thrown by a
MFC app
On Jan 6, 6:16 pm, Joseph M. Newcomer <newco...@flounder.com> wrote:
Thing * something(...)
{
Thing * result = new Thing;
... do something
return result;
}
Now what happens if an exception is thrown? We have a dangling pointer=
.. So you have to
do
Thing * something(...)
{
Thing * result = new Thing;
try {
... do something
}
catch(CException * e)
{
delete result;
throw;
}
Nah, in this trivial example, appropriate RAII class exist already.
Thing* something(...)
{
std::auto_ptr<Thing> PResult(new Thing);
...
return P.release();
}
Consider also the infamous "locking" example:
void Whatever(...)
{
Lock.Acquire(); // where Lock::~Lock releases the lock
... do something
}
(I'll presume a usual case: Lock is local to a function, there's
additional synchronization object that outlives the function, and Lock
actually operates on that one).
Note how cool! No need to worry about an internal 'return' failing to =
release the lock!
An exception releases the lock! Now consider the scenario
void Whatever(...)
{
Lock.Acquire();
.. mess around with internal data structure
FixUpInternalDataStructure(); // throws an exception
}
Note that this does: it releases the lock, saying the structure invariant=
is maintained,
when in fact the FixUpInternalDataStructure has failed to put the structu=
re back into its
correct state and thrown an exception instead. So the invariant is not=
maintained, and
the naive assumption that the code is "correct" because a nice automatic =
cleanup has
happend is violated.
You mixed two concerns: locking and data integrity. You want that
"lock is free" also means "data is consistent". Or, if you will, you
want that FixUpUnternalDataStructure has only "basic exception
safety" (http://en.wikipedia.org/wiki/
Exception_handling#Exception_safety) __and__ that locking achieve
that. That's not possible without additional work. So if that's what
you really want, you might try a scope guard and a Lock object that
outlives Whatever, e.g.:
void UnlockOnSuccess(Lock& l)
{
if (!unhandled_exception()) l.Unlock();
}
void Whatever(Lock& l)
{
l.Lock();
ON_BLOCK_EXIT(&UnlockOnSuccess, l);
FixUpUnternalDataStructure();
}
But, IMO correct approach is to __not__ mix the two concerns. That is,
FixUp... should better satisfy "commit or rollback semantics" (I am
again speaking with that Wikipedia article in mind).
(We had this discussion before; as you might remember and see here, I
believe that locking and data consistency are two different concerns
and are best handled differently).
One of the interesting problems we ran into was the notion of scope of ex=
ceptions. If I
have a type T, and there is an exception, T::x, which is a protected type=
, we cannot throw
this exception, and in fact the following code is erroneous
class T {
protected:
class x : public CException() {};
public:
void method() {
submethod();
}
protected:
void submethod() {
if(...) throw new x;
}
};
It is required that method must do a catch(x * e) because it cannot allow=
the anonymous
(outside the class) exception T::x to escape. It can simply return, or=
it can throw an
exception whose namespace is presumably visible in the caller. So it m=
ust throw a
different exception type. In our languages, exception names always had=
global scope, but
we learned to treat them as local scope names to maintain modularity.
That's only partially true. In case of MFC exceptions, it's largely
false, because they are supposed to be used polymorphically (you see
how GetErrorMessage and ReportError are virtual? what() of standard
library, too). So for the majority of cases, exact exception type
should be irrelevant, hence type visibility won't matter. That is of
course not true for, IME(xperience) rare, cases where code indeed does
want to catch a specific exception type and do something specific.
Goran.