Sunday, July 14, 2013

Exception Specifiers

In the past few years I've gotten away from using exceptions. There are a lot of reasons why- but I blame Sutter. His book "Exceptional C++" scared me for life from using exceptions. That was a few years ago so I figured it was time to face my fears about exceptions and given them another try. The first topic I want to explore is throw specifiers. 

While doing some research on the internet I found the following article from 2002 about throw specifiers [1]. The first thing read was specifying that a function does not throw anything, for example:

void doSomething() throw()
{
    // BLAH
}

This tells users that the doSomething() function shouldn't be throwing any exceptions. However, exceptions can still be thrown from doSomething() but the compiler won't do what you expect! For example, say you did the following:

void doSomething() throw()
{
    throw std::runtime_error("Exception!");
}

And then you called it- what ends up happening with g++ 4.7 is that the runtime_error gets thrown and the program crashes with: 

terminate called after throwing an instance of 'std::runtime_error'
  what(): Exception!
Aborted (coredumped)

Okay, so throw specifiers can specify that no exceptions should be thrown from a function but yet the functions can still be thrown? What kind of backwards feature is this? There's more to the confusion, try catching the exception in this form using this program:

#include <iostream>
#include <stdexcept>

void doSomething() throw()
{
    throw std::runtime_error("Exception!");
}

int main(int argc, char** argv)
{
    try 
    {
        doSomething();
    }
    catch(...)
    {
        std::cout << "CAUGHT IT!" << std::endl;
    }
}

The ellipses (...) in the catch is a catch-all. Any exceptions thrown will be caught and discarded in this program. Here's the unusual part: when a throw specification is added and an exception is thrown that is NOT specified it appears as though the program will not allow a catch in any form and it becomes an automatic crash/terminate! That's really confusing- but it's a case of exceptions behaving the way exceptions should and NOT how the programmer expects.

I guess this is just one of the strange quirks I find scary about using exceptions. To allow a runtime exception to escape you can modify the doSomething throw specification to look like:

#include <iostream>
#include <stdexcept>

void doSomething() throw(std::runtime_error)
{
    throw std::runtime_error("Exception!");
}

int main(int argc, char** argv)
{
    try 
    {
        doSomething();
    }
    catch(std::runtime_error const& err)
    {
        std::cout << "CAUGHT IT!" << std::endl;
    }
}

I also modified the catch to catch a specific exception- and notice it's done by reference. The rule of thumb is to "throw by value, catch by reference" [2]. 

REFERENCES


[2] - Effective C++, Scott Meyers. Item 13

No comments:

Post a Comment