masaj salonu masaj salonları
Home » Advertising » C++17 in Details: Attributes

C++17 in Details: Attributes

C++17 features, attributes

“C++ Attributes… what?”

Almost 40% of the votes in my recent Twitter survey went something like that. Maybe it would be good to introduce that little-known feature?

There’s even a good occasion, as in C++17 we’ll get even more useful stuff connected with attributes.

Interested?

Introduction

Have you ever used the __declspec, __attribute or #pragma directives in your code?

For example:

struct S { short f[3]; } __attribute__ ((aligned (8))); 

void fatal () __attribute__ ((noreturn));

Or for DLL import/export in MSVC:

#if COMPILING_DLL 
#define DLLEXPORT __declspec(dllexport) 
#else 
#define DLLEXPORT __declspec(dllimport) 
#endif

Those are existing forms of compiler specific attributes/annotations.

So what’s an attribute?

An attribute is an additional bit of information that can be used by the compiler to produce code. It might be utilized for optimization or some specific code generation (like DLL stuff, OpenMP, etc.).

Contrary to languages such as C#, in C++ that meta information is fixed by the compiler; you cannot add user-defined attributes. In C#, you can just ‘derive’ them from System.Attribute.

Here’s the deal about C++11 attributes:

With modern C++, we get more and more standardized attributes that will work with other compilers. So we’re moving a bit from compiler specific annotation to standard forms.

The Series

This post is the fourth in the series about C++17 features details.

The plan for the series

  1. Fixes and deprecation
  2. Language clarification
  3. Templates
  4. Attributes (today)
  5. Simplification
  6. Library changes 1
  7. Library changes 2 

Just to recall:

First of all, if you want to dig into the standard on your own, you can read the latest draft here:

N4659, 2017-03-21, Working Draft, Standard for Programming Language C++ – the link also appears on the isocpp.org.

Compiler support: C++ compiler support

There’s also a talk from Bryce Lelbach: C++Now 2017: C++17 Features

And have a look at my master C++17 features post: C++17 Features

OK, let’s go back to the main topic of this article…

Before C++11

In short: it was (and still is) a mess. 

#pragma, _declspec, __attribute… a lot of variations and compiler specific keywords.

GCC-Specific Attributes

MSVC-Specific Attributes

Clang-Specific Attributes

The document also lists what syntax is supported, so a lot of those attributes could already be used in the modern C++11 form.

Attributes in C++11 and C++14

C++11 did take one step to minimize the need to use vendor specific syntax. As I see it, the target is to move as much as is compiler specific into standardized forms.

The First thing:

With C++11, we got a nicer form of specifying annotations over our code.

The basic syntax is just [[attr]] or [[namespace::attr]].

You can use [[att]] over almost anything: types, functions, enums, etc., etc.

For example:

[[abc]] void foo() { }

In C++11, we have the following attributes:

C++14 added:

Note: There’s no need to use attributes for alignment as there’s a separate alignas keyword for that. Before, C++11, in GCC you would use __attribute__ ((aligned (N))).

Have a look at this article for more details: Modern C++ Features – Attributes.

You know a bit about the old approach, C++11/14… so what’s the deal with C++17?

C++17 Additions

With C++17, we get three more standard attributes:

  • [[fallthrough]]
  • [[nodiscard]]
  • [[maybe_unused]]

Plus three supporting features.

[[fallthrough]] Attribute

Indicates that a fall-through in a switch statement is intentional and a warning should not be issued for it.

switch (c) { 
  case 'a': 
    f(); // Warning! fallthrough is perhaps a programmer error 
  case 'b': 
    g(); 
  [[fallthrough]]; // Warning suppressed, fallthrough is ok 
  case 'c': 
    h(); 
}

More details in P0188R1 and P0068R0 – reasoning.
 GCC: 7.0, Clang: 3.9, MSVC: 15.0

[[nodiscard]] Attribute

[[nodiscard]] is used to stress that the return value of a function is not to be discarded, on pain of a compiler warning.

[[nodiscard]] int foo(); 
void bar() { 
  foo(); // Warning! return value of a 
         // nodiscard function is discarded 
}

This attribute can also be applied to types in order to mark all the functions which return that type as [[nodiscard]]:

[[nodiscard]] struct DoNotThrowMeAway{}; 
DoNotThrowMeAway i_promise(); 
void oops() { 
  i_promise(); // Warning emitted, return value of a  
               // nodiscard function is discarded 
}

More details:

GCC: 7.0, Clang: 3.9, MSVC: not yet

[[maybe_unused]] Attribute

Suppresses compiler warnings about unused entities when they are declared with [[maybe_unused]].

static void impl1() { ... } // Compilers may warn about this 
[[maybe_unused]] static void impl2() { ... } // Warning suppressed 

void foo() { 
  int x = 42; // Compilers may warn about this 
  [[maybe_unused]] int y = 42; // Warning suppressed 
}

More details:

GCC: 7.0, Clang: 3.9, MSVC: not yet

Attributes for Namespaces and Enumerators

Permits attributes on enumerators and namespaces.

enum E { 
  foobar = 0, 
  foobat [[deprecated]] = foobar 
}; 

E e = foobat; // Emits warning 

namespace [[deprecated]] old_stuff{ 
  void legacy(); 
} 

old_stuff::legacy(); // Emits warning

More details in:

GCC: 4.9 (namespaces)/ 6 (enums), Clang: 3.4, MSVC: 14.0

Ignore Unknown Attributes

That’s mostly for clarification.

Before C++17, if you tried to use some compiler specific attribute, you might even get an error when compiling in another compiler that doesn’t support it. Now, the compiler simply omits the attribute specification and won’t report anything (or just a warning). This wasn’t mentioned in the standard, so I thought it needed a little clarification.

// compilers which don't  
// support MyCompilerSpecificNamespace will ignore this attribute 
[[MyCompilerSpecificNamespace::do_special_thing]] 
void foo();

For example, in GCC 7.1 there’s a warning:

warning: 'MyCompilerSpecificNamespace::do_special_thing'
scoped attribute directive ignored [-Wattributes]
void foo(); 

More details in:

MSVC not yet, GCC: Yes, Clang: 3.9.

Using Attribute Namespaces Without Repetition

Another name for this feature was “Using non-standard attributes” in P0028R3 and PDF: P0028R2 (rationale, examples).

This simplifies the case where you want to use multiple attributes, like:

void f() { 
  [[rpr::kernel, rpr::target(cpu,gpu)]] // repetition 
  do-task(); 
}

Or a proposed change:

void f() { 
  [[using rpr: kernel, target(cpu,gpu)]] 
  do-task(); 
}

That simplification might help when building tools that automatically translate such annotated code into different programming models.

More details in P0028R4
 GCC: 7.0, Clang: 3.9, MSVC: not yet

Summary

I hope that after reading you understood the need of attributes: what are they and when are they useful. Previously each compiler could specify its own syntax and list of available attributes, but in modern C++ the committee tried to standardize this: there are some extracted, common parts. Plus each compiler is not blocked to add its own extensions. Maybe at some point, we’ll move away from __attribute or _declspec or `#pragma’?

There’s also a quite important quote from Bjarne Stroustrup’s C++11 FAQ/Attributes:

There is a reasonable fear that attributes will be used to create language dialects. The recommendation is to use attributes to only control things that do not affect the meaning of a program but might help detect errors (e.g. [[noreturn]]) or help optimizers (e.g. [[carries_dependency]]).

How about you?

What’s your experience with attributes? Do you use them? Or try to keep your code without the need to do any annotations?

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

cover letter