Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

throwing dtors...

8 views
Skip to first unread message

Chris M. Thomasson

unread,
Oct 1, 2008, 11:25:25 PM10/1/08
to
Is it every appropriate to throw in a dtor? I am thinking about a simple
example of a wrapper around a POSIX file...
________________________________________________________________________
class file {
FILE* m_handle;

public:
// [...];

~file() /* throw() */ {
int const status fclose(m_handle);
if (status) {
/* shi% hit the fan:
http://www.opengroup.org/onlinepubs/007908775/xsh/fclose.html
/*

// [what now?]
}
}
};
________________________________________________________________________


How to properly handle `EAGAIN' in dtor? Well, what about any error for that
matter? I am a C programmer and only code C++ for fun, and some in-house
projects. If I were really going to create C++ application and release it
into the wild, well, 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?

<pseudo code!!!!>
_______________________________________________________________
struct throw_from_dtor {
int const m_status;

public:
throw_from_dtor(int const status)
m_status(status) {}

int get_status() const { return m_status; }
};

class file {
FILE* m_handle;

public:
// [ctor];

~file() {
int const status = fclose(m_handle);
if (status) {
throw throw_from_dtor(status);
}
}
};


int main() {
file* f = new file();
try {
delete f;
} catch(throw_from_dtor const& e) {
// handle error from `e.get_status()'
delete f;
}
return 0;
}
_______________________________________________________________


?


or what about using smart pointer...

int main() {
std::auto_ptr<file> f;
try {
f.reset(new file());
} catch(throw_from_dtor const& e) {
// handle error from `e.get_status()'
}
}

?


Please keep in mind that refusing to not handle an error from `fclose' could
resule is HORRIBLE things down the road... Think massive data lost...
Perhaps __permanent__ data-! OUCH!!!

;^/

Chris M. Thomasson

unread,
Oct 1, 2008, 11:28:00 PM10/1/08
to

"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:k4XEk.16199$hX5....@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 to properly handle `EAGAIN' in dtor? Well, what about any error for
> that matter? I am a C programmer and only code C++ for fun, and some
> in-house projects. If I were really going to create C++ application and
> release it into the wild, well, 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?
>
>
>
> <pseudo code!!!!>
> _______________________________________________________________
> struct throw_from_dtor {
> int const m_status;
>
> public:
> throw_from_dtor(int const status)
> m_status(status) {}
>
> int get_status() const { return m_status; }
> };
>
> class file {
> FILE* m_handle;
>
> public:
> // [ctor];
>
> ~file() {
> int const status = fclose(m_handle);
> if (status) {
> throw throw_from_dtor(status);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// ummmm! well, stupid me forgot to store `errno' into the
exception!!!!!!

throw throw_from_dtor(errno);

// sorry about the non-sense! ;^(...

Chris M. Thomasson

unread,
Oct 1, 2008, 11:46:35 PM10/1/08
to
"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:k4XEk.16199$hX5....@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?


;^(...

Chris M. Thomasson

unread,
Oct 1, 2008, 11:52:00 PM10/1/08
to

"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:aoXEk.16204$hX5....@newsfe06.iad...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

let me rephrase:

So, AFAICT, throwing from a dtor will _create_ some odd complications...


Humm... I am now thinking that instead of throwing from dtor, all error
handling should be preformed within dtor... However, what if user wants to
be informed of any failure case within dtor? Should I provide a simple
callback function to inform user of such condition? Something like:
___________________________________________________________
class file {
FILE* m_handle;
bool (*m_fp_on_dtor_error) (file&, int);

public:
foo(fp_on_dtor_error fp = NULL)
: m_fp_on_dtor_error(fp) {
[...]
};

~foo() {
retry:
if (! fclose(m_handle)) {
if (m_fp_on_dtor_error) {
if (m_fp_on_dtor_error(*this, errno)) {
goto retry;
}
}
}
}
};
___________________________________________________________


Humm... I need ADVISE!

;^o

anon

unread,
Oct 2, 2008, 1:47:15 AM10/2/08
to
Chris M. Thomasson wrote:
> Is it every appropriate to throw in a dtor? I am thinking about a simple
> example of a wrapper around a POSIX file...

Take a look here:
http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.13

> ________________________________________________________________________
> class file {
> FILE* m_handle;
>
> public:
> // [...];
>
> ~file() /* throw() */ {
> int const status fclose(m_handle);
> if (status) {
> /* shi% hit the fan:
> http://www.opengroup.org/onlinepubs/007908775/xsh/fclose.html
> /*
>
> // [what now?]
> }
> }
> };
> ________________________________________________________________________
>
>
> How to properly handle `EAGAIN' in dtor? Well, what about any error for
> that matter? I am a C programmer and only code C++ for fun, and some
> in-house projects. If I were really going to create C++ application and
> release it into the wild, well, 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?
>

How would you handle it in C?

These two mains are almost the same (at least they are doing the same thing.

>
>
>
> Please keep in mind that refusing to not handle an error from `fclose'
> could resule is HORRIBLE things down the road... Think massive data
> lost... Perhaps __permanent__ data-! OUCH!!!
>

What can you do when fclose fails?

Chris M. Thomasson

unread,
Oct 2, 2008, 2:51:56 AM10/2/08
to
"anon" <an...@no.invalid> wrote in message
news:gc1n78$kks$1...@news01.versatel.de...

> Chris M. Thomasson wrote:
>> Is it every appropriate to throw in a dtor? I am thinking about a simple
>> example of a wrapper around a POSIX file...
>
> Take a look here:
> http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.13

Okay; I will give it a look.

[...]

> How would you handle it in C?

[...]

> What can you do when fclose fails?

Well, it depends on the error:

http://www.opengroup.org/onlinepubs/007908775/xsh/fclose.html

For instance, fclose can get interrupted by a signal. In this case, you need
to reissue the operation; e.g:
______________________________________________________________
struct file {
FILE* handle;
};

int file_close(
struct file* const this
) {
int status;
do {
status = fclose(this->handle);
} while (status == EOF && errno == EINTR);
return status;
}
______________________________________________________________


Or, what if it returns `EAGAIN', well, this certainly needs to be handled.
However, it would be better to let the application to handle this, not do it
implicitly within the `file_close()' function. There are many ways to handle
this. That's not the problem. The problem is when a retarded program does
not handle it! IHO, any program that does not explicitly handle errors from
`fclose()' is severely broken and VERY dangerous. Let me give you a
example... Imagine a C++ wrapper around a C FILE... Fine. Imagine the dtor
looks like this:

class file {
FILE* m_handle;

public:
~file() throw() {
fclose(m_handle);
}
};

Fine... Now, a user needs to copy a file to a disk, and destroy the
original. Okay. It creates two file objects (e.g., src and dest:)

{
file src(...);
file dest(...);

// Then it performs the copy operation:

[copy src to dest]
}

Now the code-block goes out of scope, and no exceptions were thrown during
the copy process, HOWEVER, the call to `fclose()' in the dest object
failed!!!! Well, the user thinks everything is fine because the completely
retarded ignorant moron file object did not report the fuc%ing error! So the
user operates in ignorance and happily destroys the original file thinking
that the file was COMPLETELY copied onto the disk! WRONG! The file was
partially copied because `fclose()' failed to do its thing and properly
flush the buffers, or whatever... Now, the missing file data is LOST
__forever__! OUCH!!!


This is why its ESSENTIAL to report and handle errors from `fclose()'... If
`fclose()' fails, you can't be so sure that the file is 100% coherent...


Any thoughts?

Chris M. Thomasson

unread,
Oct 2, 2008, 3:04:24 AM10/2/08
to
"anon" <an...@no.invalid> wrote in message
news:gc1n78$kks$1...@news01.versatel.de...
> Chris M. Thomasson wrote:
>> Is it every appropriate to throw in a dtor? I am thinking about a simple
>> example of a wrapper around a POSIX file...
>
> Take a look here:
> http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.13
[...]

Well, AFAICT, it seems like C++ destructors are really not all that good for
gracefully handling critical shutdown operations in dtors, such as
`fclose()'. Where am I going wrong? It seems like the only way to avoid
throwing from a dtor would be doing something along the lines of:


// example on how to handle EINTR or perhaps even EAGAIN
______________________________________________________________________
class file {
FILE* m_handle;

// returns true to retry; or false to continue...
bool (*m_fp_on_fclose_dtor) (int);

public:
file(bool (*fp_on_fclose_dtor) (int) = NULL, ...)
: m_fp_on_fclose_dtor(fp_on_fclose_dtor) {
// [...];
}

~file() throw() {
while (fclose(m_handle) == EOF) {
if (m_fp_on_fclose_dtor && !
m_fp_on_fclose_dtor(errno)) {
break;
}
}
}
};


static bool on_fclose_dtor(int status) {
switch (status) {
case EINTR:
return true;

case EAGAIN:
sleep(1);
return true;

default:
std::terminate();
}
}


int main() {
{
file f(on_fclose_dtor, ...);
// [...];
}
return 0;
}
______________________________________________________________________

Now, if a signal is thrown, or if the operation would block, the operation
will at least try to gracefully complete. All other errors KILL the program
dead... Sounds good to me.


However, this looks like a horrible hack. There has to be a MUCH between way
indeed!

:^o

Paavo Helde

unread,
Oct 2, 2008, 3:00:42 AM10/2/08
to
"Chris M. Thomasson" <n...@spam.invalid> kirjutas:

> Is it every appropriate to throw in a dtor? I am thinking about a
> simple example of a wrapper around a POSIX file...
> _______________________________________________________________________

> _ class file {


> FILE* m_handle;
>
> public:
> // [...];
>
> ~file() /* throw() */ {
> int const status fclose(m_handle);
> if (status) {
> /* shi% hit the fan:
> http://www.opengroup.org/onlinepubs/007908775/xsh/fclose.html
> /*
>
> // [what now?]
> }
> }
> };
> _______________________________________________________________________
> _
>
>
> How to properly handle `EAGAIN' in dtor? Well, what about any error
> for that matter? I am a C programmer and only code C++ for fun, and
> some in-house projects. If I were really going to create C++
> application and release it into the wild, well, 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?

Throwing from a dtor is not really advisable in C++. It can easily lead
to duplicate throws during stack unwinding, and calling terminate() as
the result.

The C++ RAII model is built up on the assumption that releasing the
resource always succeeds (or its failure can be ignored by upper levels).
If this is not the case, then the application logic becomes very complex
immediately, essentially you are back in C again.

In any case, I would suggest to move any activity which can fail out of
the destructor, into a separate member function which has to be called
explicitly before destroying of the object, possibly from inside a try-
catch block dealing with errors.

In regard of this example, for most applications, fclose() failing
indicates that the disk is full. What can you do about this? Try to
delete some random other files from the disk? For most applications I
believe a proper behavior would be to try to log the error somewhere,
then either continue or abort, depending on the application type.

If the file integrity is of the most importance, e.g. in case of a
database program, this has to be managed explicitly anyway by storing
something like transaction completion markers in the file itself, or
whatever. I bet this is not trivial.

hth
Paavo


Chris M. Thomasson

unread,
Oct 2, 2008, 3:20:40 AM10/2/08
to

"Paavo Helde" <nob...@ebi.ee> wrote in message
news:Xns9B2B65D8AF...@216.196.97.131...

> "Chris M. Thomasson" <n...@spam.invalid> kirjutas:
>
>> Is it every appropriate to throw in a dtor? I am thinking about a
>> simple example of a wrapper around a POSIX file...
>> _______________________________________________________________________
[...]

>
> Throwing from a dtor is not really advisable in C++. It can easily lead
> to duplicate throws during stack unwinding, and calling terminate() as
> the result.
>
> The C++ RAII model is built up on the assumption that releasing the
> resource always succeeds (or its failure can be ignored by upper levels).
> If this is not the case, then the application logic becomes very complex
> immediately, essentially you are back in C again.
>
> In any case, I would suggest to move any activity which can fail out of
> the destructor, into a separate member function which has to be called
> explicitly before destroying of the object, possibly from inside a try-
> catch block dealing with errors.

I think I agree here. Since, IMVHO, at least attempting to gracefully handle
`fclose()', such as deferred retrying in the case of `EINTR' or `EAGAIN', is
extremely important. Therefore, it sure seems to make sense to force the
used to explicitly call a member function which invokes `fclose()' and
throws when a very bad error its encountered (e.g., something other than
EINTR or EAGAIN).


> In regard of this example, for most applications, fclose() failing
> indicates that the disk is full.

What if the calling thread simply gets interrupted by a signal? What if the
file is non-blocking and the close operation would block? Those errors can
be handled in a straight forward manner. The former can even be handled
within the dtor itself:


class file {
FILE* m_handle;

public:
~file() throw() {
while (fclose(m_handle) == EOF && errno == EINTR);
}
};


> What can you do about this? Try to
> delete some random other files from the disk?

The application does what it has to do in order to prevent data corruption
and/or loss.


> For most applications I
> believe a proper behavior would be to try to log the error somewhere,
> then either continue or abort, depending on the application type.

What if the application needs to copy a file to disk and destroy the
original? If `fclose()' fails on the destination file, well, the application
won't know about it and will continue on and destroy the source file. Well,
the destination file is by definition in a non-coherent state because
`fclose()' failed to "do its thing". Well, the lost data is gone forever. A
log file will only show why the data was lost, it does not prevent it. In
this case I bet the user wished the application just terminated when the
`fclose()' failed. Or better, I bet the user would like to be able to catch
and explicitly handle this case...


> If the file integrity is of the most importance, e.g. in case of a
> database program, this has to be managed explicitly anyway by storing
> something like transaction completion markers in the file itself, or
> whatever. I bet this is not trivial.

Not trivial at all!

;^0

anon

unread,
Oct 2, 2008, 4:21:38 AM10/2/08
to
Chris M. Thomasson wrote:
> "anon" <an...@no.invalid> wrote in message
> news:gc1n78$kks$1...@news01.versatel.de...
>> Chris M. Thomasson wrote:
>>> Is it every appropriate to throw in a dtor? I am thinking about a
>>> simple example of a wrapper around a POSIX file...
>>
>> Take a look here:
>> http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.13
> [...]
>
> Well, AFAICT, it seems like C++ destructors are really not all that good
> for gracefully handling critical shutdown operations in dtors, such as
> `fclose()'. Where am I going wrong? It seems like the only way to avoid
> throwing from a dtor would be doing something along the lines of:
>

Destructors have to cleanup its objects, and they should (must) not fail.

>
> // example on how to handle EINTR or perhaps even EAGAIN
> ______________________________________________________________________
> class file {
> FILE* m_handle;
>
> // returns true to retry; or false to continue...
> bool (*m_fp_on_fclose_dtor) (int);
>
> public:
> file(bool (*fp_on_fclose_dtor) (int) = NULL, ...)
> : m_fp_on_fclose_dtor(fp_on_fclose_dtor) {
> // [...];
> }
>
> ~file() throw() {
> while (fclose(m_handle) == EOF) {
> if (m_fp_on_fclose_dtor && !
> m_fp_on_fclose_dtor(errno)) {
> break;
> }
> }
> }
> };
>
>

IMO This would be better:

class file {
FILE* m_handle;

public:
file()
{
// [...];
}

~file()
{
try
{
close_file();
}
// catch other exceptions
catch(...)
{
// log the error
}
}

void close_file()
{
// do whatever you can to close the file
// throw an exception in a case of an error
}

};

int main()
{
try
{
file obj;
// do stuff
obj.close_file();
}
catch(...)
{
// log error
// try to repair the damage
}
}

>
> Now, if a signal is thrown, or if the operation would block, the
> operation will at least try to gracefully complete. All other errors
> KILL the program dead... Sounds good to me.
>
>

With all other errors, your file will not be closed, and you have a
terminated program. Not very elegant solution ;)

James Kanze

unread,
Oct 2, 2008, 4:27:41 AM10/2/08
to
On Oct 2, 5:25 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> Is it every appropriate to throw in a dtor?

Sure. There are special cases where the only reason to have a
destructor is for it to throw.

All such cases are, however, special cases, and objects of those
types should only exist in special contexts (typically, as
temporaries in a single expression).

> I am thinking about a simple example of a wrapper around a
> POSIX file...

That one definitly shouldn't throw.

> ________________________________________________________________________
> class file {
> FILE* m_handle;

> public:
> // [...];

> ~file() /* throw() */ {
> int const status fclose(m_handle);
> if (status) {
> /* shi% hit the fan:
> http://www.opengroup.org/onlinepubs/007908775/xsh/fclose.html
> /*
> // [what now?]
> }
> }
> };
> ________________________________________________________________________

If you get to the destructor and the file hasn't been closed,
it's an error. It should only happen in two cases: your
unwinding the stack as a result of another error (which will
result in the generated file being deleted, or at least marked
as invalid), or there is an error elsewhere in the code (which
should result in an assertion failure).

> How to properly handle `EAGAIN' in dtor? Well, what about any
> error for that matter? I am a C programmer and only code C++
> for fun, and some in-house projects. If I were really going to
> create C++ application and release it into the wild, well, how
> would you advise me to handle the case above?

Require an explicit close by the user, before the object is
destructed, and return a return code from that function.

FWIW: most of my file output is through a file wrapper class
whose destructor deletes the file if it is called before the
file is "committed"; it also has an option for linking several
such wrappers, so that all of the files will be deleted unless
all have been successfully "committed". (Also, my shutdown
routines flush cout, and generate an error if that fails.)

[...]


> Please keep in mind that refusing to not handle an error from
> `fclose' could resule is HORRIBLE things down the road...

Obviously. And since the error must be handled, you never count
on the destructor for the close. (In the normal case---it's
fine if you're cleaning up after another error, and are going to
delete the file anyway as a result of the other error.)

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

James Kanze

unread,
Oct 2, 2008, 4:31:33 AM10/2/08
to
On Oct 2, 7:47 am, anon <a...@no.invalid> wrote:

[...]


> What can you do when fclose fails?

At the very least, you must notify the user. Generally, you'll
want to delete the file, and usually return an error code to the
OS.

About the only exception I can think of is a log file. If
closing that fails, you probably want to continue anyway,
ignoring the error. (Theoretically, you should log the
error:-).)

James Kanze

unread,
Oct 2, 2008, 4:36:19 AM10/2/08
to
On Oct 2, 9:20 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> "Paavo Helde" <nob...@ebi.ee> wrote in message

[...]


> > For most applications I believe a proper behavior would be
> > to try to log the error somewhere, then either continue or
> > abort, depending on the application type.

> What if the application needs to copy a file to disk and
> destroy the original? If `fclose()' fails on the destination
> file, well, the application won't know about it and will
> continue on and destroy the source file. Well, the
> destination file is by definition in a non-coherent state
> because `fclose()' failed to "do its thing". Well, the lost
> data is gone forever. A log file will only show why the data
> was lost, it does not prevent it. In this case I bet the user
> wished the application just terminated when the `fclose()'
> failed. Or better, I bet the user would like to be able to
> catch and explicitly handle this case...

This is why it is important in such cases to return an error
status (EXIT_FAILURE) from main. A typical idiom in Unix would
be something like:

program filename > tempFile && mv tempFile filename

And all too often, the program won't flush standard out and
verify that the flush worked. (All to many programs totally
ignore the fact that output might fail.) With the result that
if you fill up the disk, you loose important data.

Paavo Helde

unread,
Oct 2, 2008, 4:39:39 AM10/2/08
to

Exceptions are for communicating situations which cannot be dealt locally
to the upper levels in the application logic. If the error can be dealt
locally, there is no need for an exception.

I am not familiar with EAGAIN, but can't this be handled locally as well?


[...]

> What if the application needs to copy a file to disk and destroy the
> original? If `fclose()' fails on the destination file, well, the
> application won't know about it and will continue on and destroy the

Apparently, it has to know about this in this case. So fclose() result
has to be somehow communicated to the calling application. Unfortunately
it cannot be done just by throwing an exception from the destructor.
Well, in principle there is the uncaught_exception() function which one
could check in the dtor and decide whether to throw a new exception or if
there is one already in the progress, and so no new exception is needed.
However, there were some gotchas which made uncaught_exception() mostly
useless IIRC.

Paavo

Yannick Tremblay

unread,
Oct 2, 2008, 5:57:03 AM10/2/08
to
In article <X5_Ek.12946$ex3....@newsfe02.iad>,

Chris M. Thomasson <n...@spam.invalid> wrote:

That's precisely your answer:

Do exactly what you would do in C in your file_close method
and call it from your destructor:

File::~File()
{
if(0 != file_close()) {
// log the error
// or std::terminate?
}
}

int File::file_close()
{
int status = 0;
do {
status = fclose(handle);


} while (status == EOF && errno == EINTR);
return status;
}

>Or, what if it returns `EAGAIN', well, this certainly needs to be handled.

So handle EAGAIN. 2 ways:

1- in file_close()
2- if impossible in file_close() then you must require that the client do
it by explicitely calling file_close() and dealing with errors.


>Fine... Now, a user needs to copy a file to a disk, and destroy the
>original. Okay. It creates two file objects (e.g., src and dest:)
>
>{
> file src(...);
> file dest(...);
>
> // Then it performs the copy operation:
>
> [copy src to dest]
>}
>
>
>
>Now the code-block goes out of scope, and no exceptions were thrown during
>the copy process, HOWEVER, the call to `fclose()' in the dest object
>failed!!!! Well, the user thinks everything is fine because the completely
>retarded ignorant moron file object did not report the fuc%ing error! So the
>user operates in ignorance and happily destroys the original file thinking
>that the file was COMPLETELY copied onto the disk! WRONG! The file was
>partially copied because `fclose()' failed to do its thing and properly
>flush the buffers, or whatever... Now, the missing file data is LOST
>__forever__! OUCH!!!

That's a failure of the application

foo()
{
file src(...)
file dest(...)
dest.copy(src);

// Must ensure that dest was correctly flushed/sync/close *before*
// deleting src
if(0 == dest.file_close())
{
src.delete();
} else {
// whatever is needed to recover
}
}


Chris M. Thomasson

unread,
Oct 2, 2008, 8:35:05 AM10/2/08
to
"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:k4XEk.16199$hX5....@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...
[...]

Thank you all (e.g., anon, James Kanze and Paavo Helde) for your excellent
insight. After absorbing your input, I think the way to go would be
something like; thread-safety wrt the `file::m_handle' member aside for a
moment:
_________________________________________________________________
#if ! defined(FILE_DTOR_UNEXPECTED)
# define FILE_DTOR_UNEXPECTED(mp_file) assert(false)
#endif


class file {
FILE* m_handle;


public:
struct error {
struct base {
int const m_status;
base(int const status) : m_status(status) {}
};

struct bad_descriptor : public base {
bad_descriptor() : base(EBADF) {}
};

struct exceeds_offset : public base {
exceeds_offset() : base(EFBIG) {}
};

struct process_orphaned : public base {
process_orphaned() : base(EIO) {}
};

struct no_free_space : public base {
no_free_space() : base(ENOSPC) {}
};

struct pipe_not_for_reading : public base {
pipe_not_for_reading() : base(EPIPE) {}
};

struct non_existing_device : public base {
non_existing_device() : base(ENXIO) {}
};

struct already_closed : public base {
already_closed() : base(0) {}
};

struct unknown : public base {
unknown() : base(0) {}
};

static void throw_status(int const status) {
assert(! status);
switch (status) {
case EBADF:
throw bad_descriptor();
case EFBIG:
throw exceeds_offset();
case EIO:
throw process_orphaned();
case ENOSPC:
throw no_free_space();
case EPIPE:
throw pipe_not_for_reading();
case ENXIO:
throw non_existing_device();
default:
throw unknown();
}
}
};


private:
int prv_close() throw() {
int status;
do {
status = std::fclose(m_handle);


} while (status == EOF && errno == EINTR);
return status;
}


public:
bool close(bool handle_eagain = true, unsigned backoff = 1) {
if (! m_handle) {
throw error::already_closed();
}
retry:
int status = prv_close();
if (status == EOF) {
if (errno != EAGAIN) {
error::throw_status(errno);
} else if (handle_eagain) {
sleep(backoff);
goto retry;
}
return false;
}
m_handle = NULL;
return true;
}


file(/* [...] */) : m_handle(NULL) {
// [...];
}

// [...];

~file() throw() {
if (m_handle) {
if (prv_close() == EOF) {
FILE_DTOR_UNEXPECTED(m_handle);
}
}
}
};
_________________________________________________________________

And explicitly document that `file::close()' returns true if everything
worked, false if the file was non-blocking and the operation would have
blocked, or throws if shi% hit the fan. Then note that `file::close()' only
returns false when the `file::close(handle_eagain)' parameter is false.


Although James Kanze seems to suggest that `file::close()' should return a
error code; perhaps the value from errno. However, this approach requires
the user to decipher the errno value and act accordingly. Where the
fine-grain exception thing my example code uses decodes errno into specific
exception types that the user can catch. AFAICT, I think the fine-grain
exceptions are more C++'ish than return specific error codes. The fact that
`file::close()' returns a simple bool when a `EAGAIN' is encountered should
be okay.


Now an application could do something like; taking my file-copy example:
_________________________________________________________________
void foo() {
try {


file src(...);
file dest(...);
dest.copy(src);

src.close();
dest.close();
src.delete();
} catch (file::error::bad_descriptor const& e) {
// [...];
} catch (file::error::exceeds_offset const& e) {
// [...];
} catch (file::error::process_orphaned const& e) {
// [...];
} catch (file::error::no_free_space const& e) {
// [...];
} catch (file::error::pipe_not_for_reading const& e) {
// [...];
} catch (file::error::non_existing_device const& e) {
// [...];
} catch (file::error::already_closed const& e) {
// [...];
} catch (file::error::unknown const& e) {
// [...];
}
}
_________________________________________________________________

or if the application does not care which "specific" error caused things to
go bad it could do:
_________________________________________________________________
void foo() {
try {


file src(...);
file dest(...);
dest.copy(src);

src.close();
dest.close();
src.delete();
} catch (file::error::base& e) {
// [...];
}
}
_________________________________________________________________

Does that look Kosher to you C++ gurus?


;^)

James Kanze

unread,
Oct 2, 2008, 9:36:40 AM10/2/08
to
On Oct 2, 2:35 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> "Chris M. Thomasson" <n...@spam.invalid> wrote in
> messagenews:k4XEk.16199$hX5....@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...

> [...]


> Although James Kanze seems to suggest that `file::close()'
> should return a error code; perhaps the value from errno.

I said "error code" because the expression "return a return
code" sounded funny. It should return as much information as is
necessary, but not necessarily any more. Typically, it will
return a class local enum. Depending on the intended audience,
this can be as simple as:

enum ErrorCode
{
succeeded,
failed
} ;

Alternatively, you can categorize the errors in a somewhat finer
way.

In some cases, I also provide a means of accessing a system
dependent error code, in addition to the above. In such cases,
I'll also typically provide a way of mapping that error code to
a message.

Chris M. Thomasson

unread,
Oct 2, 2008, 10:03:14 AM10/2/08
to
"James Kanze" <james...@gmail.com> wrote in message
news:c9742b5f-9a15-442d...@r66g2000hsg.googlegroups.com...

> On Oct 2, 2:35 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> > "Chris M. Thomasson" <n...@spam.invalid> wrote in
> > messagenews:k4XEk.16199$hX5....@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...
>
> > [...]
> > Although James Kanze seems to suggest that `file::close()'
> > should return a error code; perhaps the value from errno.

> I said "error code" because the expression "return a return
> code" sounded funny. It should return as much information as is
> necessary, but not necessarily any more. Typically, it will
> return a class local enum. Depending on the intended audience,
> this can be as simple as:

> enum ErrorCode
> {
> succeeded,
> failed
> } ;

> Alternatively, you can categorize the errors in a somewhat finer
> way.

Humm, perhaps something like:

class file {
public:
struct error_code {
enum value {
succeeded = 0x0,
failed = 0x1,
bad_descriptor = failed | 0x2,
exceeds_offset = failed | 0x4,
process_orphaned = failed | 0x8,
no_free_space = failed | 0x10,
pipe_not_for_reading = failed | 0x20,
non_existing_device = failed | 0x40,
already_closed = failed | 0x80,
unknown = failed | 0x100
};
};
};


That way you can combine several error codes in a single value; sometimes
that ability might be fairly "useful" and/or convenient. However, IMVHO, how
does this improve on my example with fine-grain exceptions? Do C++
programmers tolerate having to examine return values for error-codes? I
think some of them would be fine with a simple boolean in certain cases, but
more than that would perhaps sound too C`ish?


> In some cases, I also provide a means of accessing a system
> dependent error code, in addition to the above. In such cases,
> I'll also typically provide a way of mapping that error code to
> a message.

Yeah; that's definitely convenient!

Andre Kostur

unread,
Oct 2, 2008, 11:50:15 AM10/2/08
to
"Chris M. Thomasson" <n...@spam.invalid> wrote in
news:X5_Ek.12946$ex3....@newsfe02.iad:

Yep, you're not using exceptions appropriately. What should be
happening is your loop around fclose should be in the destructor itself.
By throwing an exception out of the destructor you are effectively
saying that the object was unable to be destroyed. Now you're in for a
whole world of hurt. How can you possibly recover from that? The
object is already on its way out of scope, and now you can't destroy it.
You can't abort the object going out of scope. Really, if the caller
really was ready to handle errors from fclose, then they should be using
whatever method of the file class that just does fclose before allowing
the object to fall out of scope.

Additionally a whole different world of hurt happens is you have an
array of these things. Let's assume an array of 10 of them. Also let's
assume that object #7 throws an exception during construction. So the
program dutifully destroys all of the previous objects that it had
previously fully constructed. And object #3 throws an exception during
its destructor. Now you have 2 active exceptions running around.
That's a short trip to a terminated program. You can't catch it. This
is probably the best example of why allowing exceptions to propogate out
of destructors is a Bad Thing.

Yannick Tremblay

unread,
Oct 2, 2008, 11:55:45 AM10/2/08
to
In article <gq4Fk.22242$YN5....@newsfe03.iad>,

Chris M. Thomasson <n...@spam.invalid> wrote:
>
>Humm, perhaps something like:
>
>class file {
>public:
> struct error_code {
> enum value {
> succeeded = 0x0,
> failed = 0x1,
OK

> bad_descriptor = failed | 0x2,
> exceeds_offset = failed | 0x4,
> process_orphaned = failed | 0x8,
> no_free_space = failed | 0x10,
> pipe_not_for_reading = failed | 0x20,
> non_existing_device = failed | 0x40,

All fatal failures.

> already_closed = failed | 0x80,
Is this really a failure? The file is correctly
closed.

> unknown = failed | 0x100
Still a fatal failure.

>
>That way you can combine several error codes in a single value; sometimes
>that ability might be fairly "useful" and/or convenient. However, IMVHO, how
>does this improve on my example with fine-grain exceptions? Do C++
>programmers tolerate having to examine return values for error-codes? I
>think some of them would be fine with a simple boolean in certain cases, but
>more than that would perhaps sound too C`ish?

Hmm, complexity, complexity. Is that complexity necessary? Good C++
programmers like all other good programmer tolerate complexity when
complexity is needed. However, typically, the good C++ programmer is
trained in the concept of implementation hiding where unecessary
complexity is hidden away from the client code under a simpler to use
interface. If the complexity is needed for the client then the
complexity must be exposed. However, if it is not needed, it should
not be exposed.

Large application programming is in large part an exercise in
complexity management. If you try to build a house with grains of
sands, you are up against it. Bricks are much much productive.

So back in the real world :-)
Typically, one file close failure, three things can be done:
1- Try again
2- Give up and continue anyway
3- Kill the application

In very rare case, it might be possible to delete some files to free
disk space but this is rarely a possible action.

In all these cases, logging is likely to be desirable.

1- Try again can quite possibly be encapsulated in a more advanced
close function if the client doesn't have real time requirements. So
this complexity often needs not be exposed to the client.

2 or 3 are likely to be the higher level logic reaction for an
irrecoverable error regardless of the cause fo the error. If you try
to copy a file, it is irrelevant if it failed due to out of disk space
or non-existing device. It failed. In a server, you might raise an
alarm and keep going in the hope that maybe next time it will work
(maybe logrotate will have run by then or the remote device will be
back online), in another case you just exit, most probably regardless
of the cause.

The logging of the cause of the error can very easily be encapsulate
(or if exceptions are used, what() can give you a useful string).

So in my opinion, in a large number of cases where multiples unique
error codes are used, a simpler interface:

bool DoItAndTryToRecoverIfPossibleElseLogAndReturnFailure()

is sufficient. Now in your particular case, it might not be
sufficient but make sure that the complexity is really needed because
if the client code has to deal with a more complex interface, it is
more likely to have bug and will take longer to develop.

Yannick


Chris M. Thomasson

unread,
Oct 2, 2008, 1:32:33 PM10/2/08
to
"Andre Kostur" <an...@kostur.net> wrote in message
news:Xns9B2B5A829D894...@209.135.99.21...

> "Chris M. Thomasson" <n...@spam.invalid> wrote in
> news:X5_Ek.12946$ex3....@newsfe02.iad:
>
>> "anon" <an...@no.invalid> wrote in message
>> news:gc1n78$kks$1...@news01.versatel.de...
>>> Chris M. Thomasson wrote:
>>>> Is it every appropriate to throw in a dtor? I am thinking about a
>>>> simple example of a wrapper around a POSIX file...
[...]

>> Any thoughts?
>>
>
> Yep, you're not using exceptions appropriately. What should be
> happening is your loop around fclose should be in the destructor itself.

Right. I would only loop on `EINTR' in dtor, and perhaps have a bounded loop
on `EAGAIN' with a slight delay in between (e.g., sched_yield() or
something). I just would clearly document that if the user does not
explicitly close the file by invoking `file::close()' member-function then
the dtor will attempt to close, but if it fails, then no error condition
will be reported to the user at the time of dtor call.


> By throwing an exception out of the destructor you are effectively
> saying that the object was unable to be destroyed. Now you're in for a
> whole world of hurt. How can you possibly recover from that? The
> object is already on its way out of scope, and now you can't destroy it.
> You can't abort the object going out of scope. Really, if the caller
> really was ready to handle errors from fclose, then they should be using
> whatever method of the file class that just does fclose before allowing
> the object to fall out of scope.
>
> Additionally a whole different world of hurt happens is you have an
> array of these things. Let's assume an array of 10 of them. Also let's
> assume that object #7 throws an exception during construction. So the
> program dutifully destroys all of the previous objects that it had
> previously fully constructed. And object #3 throws an exception during
> its destructor. Now you have 2 active exceptions running around.
> That's a short trip to a terminated program. You can't catch it. This
> is probably the best example of why allowing exceptions to propogate out
> of destructors is a Bad Thing.

I have concluded that something like the following would work:

http://groups.google.com/group/comp.lang.c++/msg/d40f3efc3e5ee5ed

The `file::~file()' is decorated with throw(), and does not throw anything.
If the user wants to be 100% sure that `fclose()' succeeds, then she/he
needs to call `file::close()' before the object gets destroyed. Now, this
member-function can throw from a fine-grain exception hierarchy if
`fclose()' bites the dust, and the user can catch specific error conditions
and act accordingly. This seems workable to me... What do you think?

James Kanze

unread,
Oct 2, 2008, 2:49:40 PM10/2/08
to
On Oct 2, 5:55 pm, ytrem...@nyx.nyx.net (Yannick Tremblay) wrote:
> In article <gq4Fk.22242$YN5.20...@newsfe03.iad>,

> Chris M. Thomasson <n...@spam.invalid> wrote:

[...]


> So back in the real world :-)
> Typically, one file close failure, three things can be done:
> 1- Try again
> 2- Give up and continue anyway
> 3- Kill the application

> In very rare case, it might be possible to delete some files
> to free disk space but this is rarely a possible action.

I'd still want to see it provided for. More generally, if there
is a failure which the user might be able to fix (so that the
program will work next run), you want to tell him about it.
Similarly, if the failure can only be due to a programming
error, you want to abort, with a core dump and a message saying
that there was a programming error.

Chris M. Thomasson

unread,
Oct 2, 2008, 5:26:39 PM10/2/08
to

"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:UZYEk.3769$eZ6....@newsfe14.iad...
[...]

Stupid doc error!


> And explicitly document that `file::close()' returns true if everything
> worked, false if the file was non-blocking and the operation would have
> blocked, or throws if shi% hit the fan. Then note that `file::close()'
> only returns false when the `file::close(handle_eagain)' parameter is
> false.

Ummm... BIG problem... Let me add a single word, and mutate another... See
if you can notice them:


And explicitly document that `file::close()' returns true if everything
worked, false if the file was non-blocking and the operation would have
blocked, or throws if shi% hit the fan. Then note that `file::close()'

___CAN___ only return false ___IF___ the `file::close(handle_eagain)'
parameter is false.


[...]


0 new messages