Re: throwing dtors...
"Chris M. Thomasson" <no@spam.invalid> wrote in message
news:k4XEk.16199$hX5.2021@newsfe06.iad...
Is it every appropriate to throw in a dtor? I am thinking about a simple
example of a wrapper around a POSIX file...
________________________________________________________________________
[...]
________________________________________________________________________
[...]
how would you advise me to handle the case above? I am interested in how
throwing in a dtor effects dynamic destruction... Would something like the
following be legal?
[...]
I am doing some experimenting, and found that throwing from a dtor
apparently leaves the object fully intact wrt the memory that makes it up so
that proper disaster cleanup can indeed be performed... For example, the
following program goes into infinite loop:
______________________________________________________________________
#include <cstdio>
struct throw_on_dtor {};
class foo {
public:
~foo() {
throw throw_on_dtor();
}
};
int main(void) {
foo* f = new foo();
retry:
try {
delete f;
} catch (throw_on_dtor const& e) {
std::puts("throw_on_dtor caught!");
goto retry;
}
return 0;
}
______________________________________________________________________
So, AFAICT, throwing from a dtor will complicate some odd complications.
However, they can be worked out for sure. This fact that a dtor can throw
will need to be CLEARY documented indeed. The above highly crude technique
can be used to solve the fact when a file close is interrupted by a signal
(e.g., `EINTR'). It can also be used to handle `EAGAIN'... Although, it
seems eaiser to use placement new when your dealing with a class that can
throw from its dtor, so that the catch block can actually free memory
without running the dtor again like delete does... Something like:
______________________________________________________________________
#include <cstdio>
#include <cstdlib>
#include <new>
struct throw_on_dtor {};
class foo {
public:
~foo() {
throw throw_on_dtor();
}
};
int main(void) {
foo* f = new (std::malloc(sizeof(*f))) foo();
if (f) {
retry:
try {
f->~foo();
std::free(f);
} catch (throw_on_dtor const& e) {
std::puts("throw_on_dtor caught! Handling Error...");
std::free(f);
}
}
return 0;
}
______________________________________________________________________
Humm... The placement new soultion looks like a good match for throwing
dtors indeed!
Also, a class which throws from dtors could contain a dtor counter and/or
flag to detect how many times, if any, the dtor has been invoked; something
like:
class foo {
unsigned m_dtor_invoke; // = 0
bool m_dtor_throw; // = false;
public:
~foo() {
++m_dtor_invoke;
if (! m_dtor_throw) {
m_dtor_throw = true;
throw throw_on_dtor();
}
}
};
Any thoughts? BTW, try not to flame me too harshly! I am trying to avoid the
user explicitly calling a close function... Or, is that a great idea wrt
dealing with any class that has a dtor which calls an API that can fail
_AND_ such failure indicates something important?
;^(...