I sometimes wonder how to handle construction of objects which can throw in their constructor. I wonder how you do it.
Consider the following snippet. I have a class, named TCPMessage, which represents a message my “server” receives over TCP. If the received message is invalid (i.e. the CRC32 calculated in the TCPMessage‘s constructor doesn’t check out), TCPMessage‘s constructor throws.
So below is how I do it. Do you know of any better way? I’m asking, because it doesn’t really look too elegant.
void TCPConnection::handleRead(
const boost::system::error_code& error,
char* read_buffer
)
{
if (!error) {
TCPMessage* message = NULL; // being verbose here
try {
message = new TCPMessage(read_buffer);
} catch (const char* e) {
std::cerr << "Instatiating TCPMessage: " << e << std::endl;
} catch (...) {
std::cerr << "Instatiating TCPMessage: unknown exception." << std::endl;
}
if (message) {
// if created succesfully
// process the message and delete it
SeekurJrRC::Core::Driver& driver = boost::asio::use_service<SeekurJrRC::Core::Driver>(_io_service);
driver.processMessage(*message);
delete message;
}
}
delete [] read_buffer;
}
Oh yeah, an I know about knowing better than to use char* read_buffer and deleting it in another function. shared_ptr‘s the way, I know.
Your problem isn’t exceptions, it’s raw pointers and lack of RAII.
Sanitizing your code a bit:
newcalls should be wrapped in RAII objects, not dangle around freely in your user code.deletecalls should never be explicit, but instead be handled by the destructors defined in your RAII objects.Then your objects will automatically get destroyed and clean up after themselves if an exception is thrown, and you don’t need your over-complicated “try/catch/check-for-success” dance. If an exception was not thrown, you continue normally. If it was thrown, you leave the
tryblock, and your objects are destroyed automatically.Note that here you don’t actually need the
try/catchblock any more. The only thing you use thecatchfor is to print an error message. It’s not necessary for the program flow or to prevent resource leaks. Normally you would handle the error where you can meaningfully do so. Presumably that’s somewhat higher up the call tree, where you know what to do about a failed read. At this level, it makes more sense to just let the exception escape to indicate that an error occurred.