devxlogo

C++09 Attributes: Specify Your Constructs’ Unusual Properties

C++09 Attributes: Specify Your Constructs’ Unusual Properties

++ constructs such as functions, types, variables, and blocks are associated with implicit properties that you sometimes need to override. Certain constructs require you to specify their unusual properties, for example:

  • A function that never returns
  • A type with a particular alignment restriction
  • A virtual function that must not be overridden

Attributes, a new C++09 feature, are compile-time tokens that enable you to designate properties like these. By using attributes, you can specify additional information about your programming constructs conveniently and portably. This 10-Minute Solution shows how to apply this technique.


You need to tell the compiler and your fellow programmers how to use a certain component, but you don’t want to use nonstandard features or risk inadvertent usage violations.


Use C++09 attributes.

final, Finally

Before the advent of C++09, ensuring that clients could not override a certain virtual function any further was a struggle. Consider the following example:

struct B { virtual void f (); //do not override this!};struct D : B { void f(); //overriding B::f, uncaught by the compiler};

The author of class B made her intentions clear: Any class derived from B must not override f(). Still, an innocent user who derived D from Bmistakenly violated this requirement. A standard C++ compiler cannot detect this violation because all public virtual functions can be overridden by default.

C++09 attributes tackle this problem. An attribute is a token enclosed between double square brackets, for example [[myattribute]]. C++09 defines several attribute tokens, which you need to understand aren’t reserved keywords. To indicate that a virtual function must not be overridden any further, you need to appertain the attribute [[final]]to the last permitted override of that function:

struct B //C++09 only { virtual void f [[final]] ();//can't override f() any further!};struct D : B { void f(); // compilation error: attempt to override B::f};

The [[final]] attribute applies to class definitions and to virtual member functions declared in a class definition. If the [[final]] attribute is specified for a class definition, it’s equivalent to being specified for each virtual member function of that class, including inherited member functions. The following declaration of class A

struct A [[final]] //C++09 only{ virtual void f(); virtual void g(); virtual void h();};

…is equivalent to:

struct A  //C++09 only{ virtual void f [[final]] (); virtual void g [[final]] (); virtual void h [[final]] ();};

The Point of noreturn

Certain functions can exhibit some unusual behavior. For instance, functions that never return to their caller are rather common in applications that use signals and exceptions. Additionally, some odd beasts such as the POSIX fork()function have the unusual property of returning not once per invocation but twice: once in the parent and once in the child.

Programmers need to be alerted of such unusual behavior when they encounter these functions. Furthermore, compilers often issue a warning when they detect a function call that never returns. Once again, C++09 attributes come to rescue, making your code easier to understand and more secure.

To designate a function that never returns, use the attribute-token noreturn and make sure the first declaration of the function specifies the noreturn attribute. If you declare a function with the noreturn attribute in one translation unit and then declare it without the noreturnattribute in another translation unit, your program will be ill formed.

Here’s an example of a noreturnfunction:

void f [[ noreturn ]] () { throw "failed"; // OK}

GCC users may recognize the [[noreturn]] attribute as the equivalent of __noreturn__:

//GCC specific void die(int, void *) __attribute__((__noreturn__)); //GCC

In C++09, you would declare the same function like this:

void die [[ noreturn ]] (int, void *); //C++09

Alignment Revisited

In a previous 10-Minute Solution, I showed how to probe and override the alignment of types and objects. Newsflash: The latest C++09 standard got rid of the alignas keyword (but the alignofoperator remains in the standard).

If you want to override the default alignment of a type or an object, or you want to ensure that the same alignment is used in every environment, use the [[align]] attribute. The align token takes a type or an assignment expression as its argument. (Certain attributes can have an argument clause enclosed in parentheses.) For example, here’s how you declare a char array that is suitably aligned for double:

//byte array suitably aligned for doubleunsigned char c [[align(double)]] [sizeof(double)];

When multiple alignment attributes are specified for an object, the alignment requirement is set to the strictest (highest) specified attribute. Here is how you would declare a buffer with an alignment requirement of A, holding N elements of type T:

//choose the strictest alignment of the two T buffer [[align(T),align(A)]] [N];

Tribute to Attributes

Presently, C++09 defines four attribute tokens. Although I don’t discuss the details in this article, the fourth token, carries_dependency, specifies dependency propagation into and out of functions. The standard allows implementations to extend the token system by defining compiler-specific attribute tokens. Thus, a hypothetical POSIX compiler may define an attribute token [[returns_twice]], which appertains to the fork()function as follows:

pid_t  fork [[returns_twice]] (void);

Look for a deeper examination of the carries_dependencytoken in an upcoming 10-Minute Solution. In the meantime, use attributes to alert the compiler and your fellow programmers to the unusual behavior of some of your programming constructs.

devx-admin

Share the Post: