Using C++17 Attributes Today

C++11 added generalized attributes to the standard, a way to specify extra information to the compiler about an expression. Previously different compilers exposed a different mechanism to achieve this goal, __attribute__((deprecated)) on GCC/clang and __declspec(deprecated) in Visual C++. Generalized attributes, or attribute specifier sequences, allow you to express this is the compiler agnostic form [[deprecated]] .

The depcrecated attribute was standardized in C++14 however GCC, clang, and Visual C++ already supported it in some form. For portable C++11 code the simple form must be used, this does not take a string describing of why the feature was deprecated.

New Attributes in C++17

With C++17 three additional attributes were added to the standard which enable additional compiler warnings when used in general code.

nodiscard specifies that a return value should not be discarded

specifies that a return value should not be discarded fallthrough indicates to the compiler that you intended a case in a switch statement to fall-through, requires an optional warning to be enabled

indicates to the compiler that you intended a case in a switch statement to fall-through, requires an optional warning to be enabled maybe_unused silences warnings generated when a variable is only used in an assertion

Unfortunately at the time of writing C++17 compilers are not available yet. Even if they were it still could be many years before developers actually get to use one in a production environment. Happily the C++ standards committee did not pull these concepts out of thin air, in fact some compilers support equivalent attributes.

How To Use Them

Here’s the caveat, I will be focusing on attributes provided by GCC and clang. If you use one of these compilers for development or support them in your continuous integration system you can still reap the benefits.

Attribute Detection

Since not all compilers will support these attributes we need a mechanism to detect if any given attribute is supported. Fortunately clang and GCC provide such a mechanism, __has_cpp_attribute according to these guidelines. Support was added to clang in version 3.6 and GCC in version 5.

To use the __has_cpp_attribute function like macro at the same time as targeting compilers which don’t support it yet the following guarded definition is useful as described here.

Using the nodiscard Attribute

It is not unusual to disable exceptions in a project, usually because of the performance implications when an error occurs. This is common practice in game development and in the LLVM compiler infrastructure project, which clang is apart of. Usually in such code bases error codes are used to notify the caller of a function that a recoverable error has occurred. It is however easy to forget to check error codes, this is where the nodiscard attribute comes in.

Specifying the nodiscard attribute can be done on a function definition.

int [[nodiscard]] foo() { } foo();

But if you are using a class for you error reporting the attribute can be specified on the class definition in just one place instead.

class [[nodiscard]] error { }; error bar () ; bar();

This is especially useful when compiling with warnings as errors as now it is possible to ensure error conditions are being handled at compile time.

The nodiscard attribute came to life with another name gnu::warn_unused_result which was introduced to GCC a number of years ago and to clang in version 3.6. Using attribute detection now we are able to use nodiscard functionality on a larger proportion of currently available compilers and projects and use the C++17 attributes when available.

Now we can use this to update the definitions from the example above.

int NODISCARD foo () { } class NODISCARD error { };

Using the fallthrough Attribute

An common gripe with switch statements is that adding break to a case is not implicit. Whilst the language doesn’t support implicitly breaking the compiler can help. The fallthrough attribute originated in clang with the name clang::fallthrough in version 3.6. GCC will add support in version 7 which is still in development at the time of writing.

To take advantage of the fallthrough attribute the -Wimplicit-fallthrough warning must be enabled. Once we have this warning enabled case statements which do not contain a break or the fallthrough attribute will generate a warning.

enum class option { A, B, C }; void choice (option value) { switch (value) { case option::A: case option::B: FALLTHROUGH; case option::C: break ; } }

Note that the fallthrough attribute must be a null statement, that is it must be an expression on its own line at the end of the case statement.

Using the maybe_unused Attribute

It is common practice to sprinkle code with assert 's to ensure you catch unexpected inputs during development. Sometimes this results in a variable being created which is only used with in the assert itself. This can result in warnings being produced when assertions are disabled, I’ve seen this break the build many times when compiling with warnings as errors. Silencing these warnings is the intention of the maybe_unused attribute.

But wait, there is already a solution for this. The traditional way to silence these warnings is to do the following.

int value = 42 ; ( void )value; assert(value == 42 );

To get the same behaviour using maybe_unused we don’t need a separate statement, but first here is the detection code.

Now to disable the unused variable warning when assertions are disabled do the following with one line of code less whilst adding also readability.

MAYBE_UNUSED int value = 42 ; assert(value == 42 );

Conclusion

Compiler support for C++17 standard attributes should be coming along soon, in fact version 3.9 of clang has enabled them in C++11 mode and upwards. For those without access to the latest compilers it can still be possible to get access to these little known but useful features described here.