"Chris M. Thomasson" <no@spam.invali dwrote in message
news:k4******** **********@news fe06.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_DT OR_UNEXPECTED)
# define FILE_DTOR_UNEXP ECTED(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_orphane d : public base {
process_orphane d() : base(EIO) {}
};
struct no_free_space : public base {
no_free_space() : base(ENOSPC) {}
};
struct pipe_not_for_re ading : public base {
pipe_not_for_re ading() : base(EPIPE) {}
};
struct non_existing_de vice : public base {
non_existing_de vice() : base(ENXIO) {}
};
struct already_closed : public base {
already_closed( ) : base(0) {}
};
struct unknown : public base {
unknown() : base(0) {}
};
static void throw_status(in t const status) {
assert(! status);
switch (status) {
case EBADF:
throw bad_descriptor( );
case EFBIG:
throw exceeds_offset( );
case EIO:
throw process_orphane d();
case ENOSPC:
throw no_free_space() ;
case EPIPE:
throw pipe_not_for_re ading();
case ENXIO:
throw non_existing_de vice();
default:
throw unknown();
}
}
};
private:
int prv_close() throw() {
int status;
do {
status = std::fclose(m_h andle);
} 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_st atus(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_UNEXP ECTED(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(ha ndle_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::b ad_descriptor const& e) {
// [...];
} catch (file::error::e xceeds_offset const& e) {
// [...];
} catch (file::error::p rocess_orphaned const& e) {
// [...];
} catch (file::error::n o_free_space const& e) {
// [...];
} catch (file::error::p ipe_not_for_rea ding const& e) {
// [...];
} catch (file::error::n on_existing_dev ice const& e) {
// [...];
} catch (file::error::a lready_closed const& e) {
// [...];
} catch (file::error::u nknown 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::b ase& e) {
// [...];
}
}
_______________ _______________ _______________ _______________ _____
Does that look Kosher to you C++ gurus?
;^)