Improve Exception Safety In Your C++ Applications by Annotating Functions That Don't Throw Exceptions

Introduction

In the early days of C++ programming, exception specifications were used frequently. However, they have lost their appeal for various reasons and are now heading for deprecation, except for the special case of the empty exception specification. C++0x recently introduced the attribute [[nothrow]] to designate a function that doesn't throw an exception. In the following sections I will explain how [[nothrow]] can help you optimize the compiler's generated code and make your code self- explanatory.

The Problem

You're designing a function that never throws. You want to document this property so that the compiler can optimize the generated machine code.

The Solution

Use the new [[nothrow]] attribute.

The C++98 Approach

In C++98, you annotate a function that never throws by appending an empty exception specification to its declarations:

   void freeze() throw(); //free function 
   
   struct A {
    explicit A(int i) throw();//member functions
    virtual void f() throw();
   };
   template <typename T> 
    T min (T x, T y) throw(); //function template
 

The throw() clause after the function's parameter list indicates that the function will not terminate by throwing an exception, either directly (due to an explicit throw statement), or indirectly (by calling another function that throws). Seemingly, the throw() clause serves your purpose--it allows the compiler to optimize the generated code. However, most programmers abstain from exception specifications completely:

void freeze(); //doesn't throw, but no exception specification

What you need is a standard mechanism that will communicate to the compiler and your fellow programmers that your function never throws. This is what the [[nothrow]] attribute does.

No Exceptions Allowed

Author's note: I described the C++0x attributes mechanism in two former C++ 10 Minute Solutions. The first one introduced the notion of attributes. The second Solution focused on safe overriding of virtual member functions. If you're not familiar with C++0x attributes, you're advised to read these 10 Minutes Solutions in chronological order.

The new attribute may pertain to the following callable entities:

  • free function
  • member function
  • function template
  • member function template

If [[nothrow]] pertains to a template, it applies to every specialization of that template:

  template <typename T> 
   T min (T x, T y) [[nothrow]]();  
  //specializations
  int n=min(5,10); //min<int> declared [[nothrow]]
  double d=min(5.,10.9); //min<double> declared [[nothrow]]

Syntactically, [[nothrow]] follows the same rules of other standard attributes. However, the standards committee recently revised the attributes mechanism to ensure simpler and more intuitive usage. I will therefore take advantage of the [[nothrow]] attribute to demonstrate some of the recent changes to the attributes system.

The early attributes specification insisted that an attribute should appear after the entity to which it pertains. Thus, function attributes were allowed only after the function's name:

  //Earlier attributes specification
  void terminate [[noreturn]] (); //OK
  [[noreturn]] 
  void terminate(); //error, attribute in wrong position

This restriction was recently relaxed. You can now place an attribute before the function's declaration, similar to the inline, explicit and virtual specifiers:

  //Latest C++0x attributes specification
  void terminate [[noreturn]] (); //OK
  [[noreturn]] void terminate(); //OK too

The latest attributes specification also allows you to pack multiple attribute tokens separated by commas:

  [[noreturn,nothrow]] void kill(); //OK 
  void kill [[noreturn,nothrow]](); //OK

However, repeating the same token within an attribute clause is not allowed:

[[noreturn,noreturn]] void kill(); //error, repeated token
[[noreturn]] void kill [[noreturn]] (); //OK but redundant

If a callable entity is declared [[nothrow]], the first declaration of that entity in that translation unit shall specify the [[nothrow]] attribute. Likewise, if an entity is declared [[nothrow]] in one translation unit, every translation unit that declares that entity shall also declare it [[nothrow]]. These rules are meant to prevent ODR violations. Consider:

  //file f.h
  void f();
  void f [[nothrow]](); //error, no attr in first declaration

Virtual functions are of special interest. If a member function overrides a virtual base class member function that was declared [[nothrow]], the overriding function shall also be declared [[nothrow]]. Otherwise, the program is ill-formed:

  struct A { virtual void func(); };
  struct B { [[nothrow]] virtual void func(); };
  struct D: A, B{
   void func(); // error, [[nothrow]] required
  };

As with the throw() clause, if a call to an entity declared [[nothrow]] terminates with an exception, the program's behavior is undefined:

  [[nothrow]] void g()
  {
   throw "trap"; //violates the [[nothrow]] guarantee
  }
  int main() 
  {
   g(); // undefined behavior; an exception is thrown
  }

Taking Exceptions

The [[nothrow]] attribute is meant primarily as a hint for compilers. When a compiler sees an entity declared [[nothrow]] it can in some cases omit the implicit try/catch constructs that are needed for implementing exception specifications. Therefore, use the [[nothrow]] attribute only of you're certain that a function will not throw an exception. In addition to the code optimization benefit, [[nothrow]] also instructs clients that it's safe call that function without a try block.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds