Search

14.7 — Function try blocks

Try and catch blocks work well enough in most cases, but there is one particular case in which they are not sufficient. Consider the following example:

In the above example, derived class B calls base class constructor A, which can throw an exception. Because the creation of object b has been placed inside a try block (in function main()), if A throws an exception, main’s try block will catch it. Consequently, this program prints:

Oops

But what if we want to catch the exception inside of B? The call to base constructor A happens via the member initialization list, before the B constructor’s body is called. There’s no way to wrap a standard try block around it.

In this situation, we have to use a slightly modified try block called a function try block.

Function try blocks

Function try blocks are designed to allow you to establish an exception handler around the body of an entire function, rather than around a block of code.

The syntax for function try blocks is a little hard to describe, so we’ll show by example:

When this program is run, it produces the output:

Exception caught
Oops

Let’s examine this program in more detail.

First, note the addition of the “try” keyword before the member initializer list. This indicates that everything after that point (until the end of the function) should be considered inside of the try block.

Second, note that the associated catch block is at the same level of indentation as the entire function. Any exception thrown between the try keyword and the end of the function body will be eligible to be caught here.

Finally, unlike normal catch blocks, which allow you to either resolve an exception, throw a new exception, or rethrow an existing exception, with function-level try blocks, you must throw or rethrow an exception. If you do not explicitly throw a new exception, or rethrow the current exception (using the throw keyword by itself), the exception will be implicitly rethrown up the stack.

In the program above, because we did not explicitly throw an exception from the function-level catch block, the exception was implicitly rethrown, and was caught by the catch block in main(). This is the reason why the above program prints “Oops”!

Although function level try blocks can be used with non-member functions as well, they typically aren’t because there’s rarely a case where this would be needed. They are almost exclusively used with constructors!

Function try blocks can catch both base and the current class exceptions

In the above example, if either A or B’s constructor throw an exception, it will be caught by the try block around B’s constructor.

We can see that in the following example, where we’re throwing an exception from class B instead of class A:

We get the same output:

Exception caught
Oops

Don’t use function try to clean up resources

When construction of an object fails, the destructor of the class is not called. Consequently, you may be tempted to use a function try block as a way to clean up a class that had partially allocated resources before failing. However, referring to members of the failed object is considered undefined behavior since the object is “dead” before the catch block executes. This means that you can’t use function try to clean up after a class. If you want to clean up after a class, follow the standard rules for cleaning up classes that throw exceptions (see the “When constructor fail” subsection of lesson 14.5 -- Exceptions, classes, and inheritance).

Function try is useful primarily for either logging failures before passing the exception up the stack, or for changing the type of exception thrown.

14.8 -- Exception dangers and downsides
Index
14.6 -- Rethrowing exceptions

15 comments to 14.7 — Function try blocks

  • Vir1oN

    Hi

    I was playing with your first code example to learn more about what will happen with the derived class (B), when the base class` constructor throws an exception. I was expecting to see the same behavior as described in lesson 14.5 in the section "When constructors fail", but came into the different result.

    Could you please point out what I`m missing here?

    • `A` gets initialized before `member` gets initialized. Since the construction of `A` fails, you never initialize `member`.

      You can check out the full order at cppreference
      https://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order

  • in the last example the statement "std::cerr << "Construction of A failed\n";" is restricted to failure in constructing A, but function try block can also fail in constructing the body of B, is my understanding correct or is only used for constructor A ?

    • Alex

      It will also catch exceptions in B's constructor. I've updated the lesson to make this explicit. Thanks for pointing out the lack of clarity.

  • Asgar

    Hi. I am confused by the last paragraph. Kindly consider this case and please explain how we would clean-up a  broken, "half-created" instance of B, since you say a function try-block should not be used for clean-up purposes:

    You mention that a constructor is responsible for clean-up if object creation fails, before raising an exception, since a destructor will not be invoked once an exception is raised. How would we equipment B's constructor to do that?

    • Alex

      B's constructor can't do that. Class A has to clean up after itself. As for how it should do so, I added a bit of text to lesson 14.5 talking more about how classes should clean up in the light of a failed construction (including an example). See lesson 14.5, "when constructors fail" subsection.

  • Hugh Mungus

    Hey, Alex, why do function try blocks have to pass the exception to the caller? Is there any reason why it can't be handled as soon as it's caught?

    • Alex

      Consider the case where you have a Base and Derived class. Now let's say Base throws an exception, and Derived catches it with a function try block. If Derived handled the exception, the caller would never know that Base had failed and might not be properly initialized. Alternatively, if you had a Base, Derived, and Last class, and Derived handled the exception, Last wouldn't know Base had failed, and might try to access something in Base.

  • Hardik

    So, Alex, How should we prevent Function-level Try Blocks From re-throwing the same exception?
    Can you suggest any FIX?

  • susenj

    Alex, what did you mean when you said in the last line "They are almost exclusive used with derived constructors!" ?

    Thanks for the great work, btw.

    • Alex

      I meant to say, "They are almost exclusively used with derived constructors!" (I missed the -ly suffix). Meaning they're used in that particular case only.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]