Archive for the ‘C++’ Category

C++11 generalized attributes

Wednesday, April 18th, 2012

Generalized attributes are a new C++11 core language feature that is not much talked about. The reason for this is because attributes are not easily extendable by the user. In their current form, one cannot define custom attributes without also having to modify the C++ compiler in one way or another to support them (more on this later). Rather, their official purpose is to allow future C++ extensions without needing to add new keywords and augment the language grammar. But, I think, it is fairly reasonable to expect that attributes will also be used for proprietary extensions as well as to embed domain-specific languages (DSL) into C++. In fact, even the original proposal for this feature suggested that attributes can be used to handle OpenMP parallelism annotations.

The ability to create a C++-embedded DSL is what got me interested in the generalized attributes in the first place. ODB (project I am currently working on) is a compiler-based C++ ORM that uses a #pragma-based DSL to embed database-related information, such as table and column names, database types, etc., into C++ classes. While the #pragma approach works fairly well, it has its drawback, mainly the fact that pragmas cannot mix with C++ constructs; they always have to appear on a separate line. In this respect, C++11 generalized attributes seemed much more flexible since they are part of the language proper. So I decided to explore this feature in more detail to see if it can be considered as a potential future replacement for pragmas in ODB.

But first, let’s see what the generalized attributes are all about. As I mentioned above, there is not much information on this feature. The two primary sources are the original proposal paper called Towards support for attributes in C++ and the C++11 standard itself. The original proposal paper went through many revisions (the link above is to revision 6, which seems to be the last). While the proposed wording changes for the standard in the latest revision are almost (but not exactly) what ended up in the standard, the rest of the paper (discussion, examples, etc.), hasn’t been updated and in many cases is incorrect per the published standard. The problem with the standard is that it is dry, uses its own terminology (what’s a declarator-id?) and often lacks motivation and examples. In the case of attributes, the changes are spread over many chapters making it difficult to see the whole picture.

I spent quite a bit of time going back and forth between the various revisions of the paper and the standard trying to put the pieces together. The result is what I hope is a more approachable description of the C++11 generalized attributes.

Attributes can be specified for almost any C++ construct. A single attribute or a comma-separated list of attributes must be enclosed into double square brackets. For example:

int x [[foo]];          // foo applies to variable x
void f [[foo, bar]] (); // foo and bar apply to function f

An attribute name can be optionally qualified with a single-level attribute namespace and followed by attribute arguments enclosed in parenthesis. The format of attribute arguments is attribute-dependant. For example:

int x [[omp::shared]];
 
[[likely(true)]] if (x == 0)
{
  ...
}

Let’s now look at how to specify attributes for various C++ constructs. When we introduce or refer to a named entity, then, with a few exceptions, an attribute applies (or appertains, in the C++ standard terminology) to the immediately preceding entity. In other cases, attributes normally apply to the following entity. Not very clear, I know. In my experience, the best way forward is to get an intuitive understanding by looking at enough examples. So let’s start with the named entities, which are variables, functions, and types. For variables and functions, including function parameters, the attribute is specified after the name (called declarator-id in the standard):

int x [[foo]], y [[bar]]; // foo applies to x,
                          // bar applies to y
 
int f [[foo]] (int i[[bar]]); // foo applies to f
                              // bar applies to i

But we can also specify an attribute at the beginning of the declaration, in which case it applies to all the names (this is done to support syntax similar to the storage class specifier, such as static or thread_local). For example:

[[omp::shared]] int x, y; // omp::shared applies to both
                          // x and y

Ok, let’s now look at types. Things are a bit more complicated here and we will start with references to types (just to be clear, by reference here I mean “referring to a previously-declared type using its name” rather that forming a reference, as in “l-value reference”). Similar to variables and functions, an attribute for a reference to a type is specified at the end. For example:

int [[foo]] x; // foo applies to x's type

Note that such an attribute affects a type only for the declaration in which it appears. In other words, the above declaration doesn’t attach attribute foo to the fundamental type int but rather to the type of variable x. Here is another example that illustrates this point:

int [[foo]] x, y; // foo applies to x's and y's type
int z;            // foo does not apply to z's type

If we want to create a type with an attribute, then we can use the typedef or alias declaration:

typedef int [[foo]] int_foo;
using int_foo = int [[foo]];

Interestingly, the above two declarations can also be written like this, which should have the same semantics:

typedef int int_foo [[foo]];
using int_foo [[foo]] = int;

If we are declaring our own class type, then we can also specify its attributes (a round-about way to achieve the same would be to first declare it without any attributes and then use the typedef or alias declaration to add them). You would expect that attributes come after a class name or a class body but here we have another exception: the attributes come after the class keyword and before the class name (or body, if there is no name). Here is an example:

class [[foo]] c
{
};

Putting all of the above together we can have quite an elaborate attribute sequence:

[[attr1]] class [[attr2]] c {...} [[attr3]] x [[attr4]], y;
 
// attr1 applies to variables x and y
// attr2 applies to class c
// attr3 applies to x's and y's types
// attr4 applies to variable x

Ok, those are the rules for named entities such as variables, functions, and types. Attributes can also be specified for simple statements, blocks, as well as selection (if, switch), iteration (for, while, do), and jump (break, continue, return) statements. In all these cases, attributes come first. For example

[[attr1]] for (...) // attr1 applies to for-loop
[[attr2]] {         // attr2 applies to block
  [[attr3]] f ();   // attr3 applies to statement
  [[attr4]] break;  // attr4 applies to break
}

Finally, attributes can appear on their own in the namespace scope, for example:

[[attr1]];
 
namespace n
{
  [[attr2]];
}

What do such attributes apply to? The standard seems to indicate that this is attribute-dependant. For example, we can have attributes that always apply to the translation unit, or to the current namespace, or even to the declaration immediately following them.

These are the C++ constructs for which you will most likely encounter or want to add an attribute. There are other less-likely places, such as the base class specification, as well as the case and default labels. Interestingly, one feature that was left attribute-unaware is namespaces. In particular, we won’t be able to write something like this:

namespace [[visibility(hidden)]] n
{
}

My guess is that the standard committee felt that namespace-scope attributes will be sufficient to provide a similar functionality:

namespace n
{
  [[visibility(hidden)]];
}

Let’s now get back to the idea of using attributes to embed a domain-specific language (DSL) into C++. To add support for an attribute we would need to modify the compiler. While this probably won’t be possible any time soon for, say VC++, things are much more promising in the open-source world. GCC had support for compiler plugins since version 4.5.0 (if you are interested, I wrote a series of posts on parsing C++ with GCC plugins). Besides other things, a plugin can register custom pragmas and GCC attributes. I am sure when GCC supports C++11 attributes, it will be possible to register custom ones as well. Clang will probably have a similar mechanism.

Note that this doesn’t mean our DSL has to be limited to just these compilers. If we do some kind of static analysis, then nothing prevents us, for example, from using a GCC-based analyzer in a VC++-based project. Even if the purpose of our DSL is to provide some additional functionality to the resulting application (e.g., persistence or extended type information), then it can be portable if we make it a pre-compiler with standard C++ as its output. This is exactly how ODB works. The GCC-based ODB compiler compiles a header that defines a class and generates a set of C++ files that contain database persistence code for this class. This code can then be compiled with any C++ compiler, including VC++.

Finally, to get a sense of attributes’ usability, I compared a pragma-based DSL example that is currently implemented in ODB to a hypothetical C++11 attribute-based version. A typical persistent class declaration in ODB might look like this:

#pragma db object
class person
{
  ...
 
  #pragma db id auto
  unsigned long id_;
 
  std::string first_;
  std::string last_;
};

And an attribute based approach could look like this:

class [[db::object]] person
{
  ...
 
  unsigned long id_ [[db::id, db::auto]];
  std::string first_;
  std::string last_;
};

It definitely looks tidier. And here is how they compare if we add a bit more customization:

#pragma db object table("people")
class person
{
  ...
 
  #pragma db id auto type("INT")
  unsigned long id_;
 
  #pragma db column("first_name")
  std::string first_;
 
  #pragma db column("last_name")
  std::string last_;
};

And the attribute based approach:

class [[db::object, db::table("people"))]] person
{
  ...
 
  unsigned long id_ [[db::id, db::auto, db::type("INT")]];
  std::string first_ [[db::column("first_name")]];
  std::string last_ [[db::column("last_name")]];
};

Which one do you like better?

When to provide an empty destructor

Wednesday, April 4th, 2012

If you search around on the Internet, you will find various opinions about whether it is a good idea to provide an explicit empty definition of a destructor or if it is best to let the compiler synthesize an implementation for you. The other day I also caught myself thinking about this choice for a class I’ve been working on. This made me realize that I don’t have a complete and clear picture of the tradeoffs involved. Ideally, I would like a hard and fast rule so that I don’t have to waste a few minutes thinking about this every time I create a new class. So today I decided to lay this matter to rest by analyzing all the common and special cases that I am aware of while taking into account not only performance, but also footprint and even the compilation time.

There are three distinct use-cases that I would like to analyze: a class or a class template with a non-virtual destructor, a class with a virtual destructor, and a class template with a virtual destructor. But before we jump to the analysis, let’s first review some terms used by the standard when talking about synthesized destructors. At the end of the analysis I would also like to mention some special but fairly common cases as well as how C++11 helps with the situation.

If we declare our own destructor, the standard calls it a user-declared destructor. If we declared a destructor, we also have to define it at some point. If a class has no user-declared destructor, one is declared implicitly by the compiler and is called an implicitly-declared destructor. An implicitly-declared destructor is inline. An implicitly-declared destructor is called trivial, if (a) it is not virtual, (b) all its base classes have trivial destructors, and (c) all its non-static data members have trivial destructors. In other words, a trivial destructor doesn’t need to execute any instructions and, as a result, doesn’t need to be called, or even exist in the program text. Note that the first condition (that a destructor shall not be virtual) was only added in C++11, but, practically, I believe all the implementations assumed this even for C++98 (virtual function table contains a pointer to the virtual destructor and one can’t point to something that doesn’t exist).

Another aspect about destructors that is important to understand is that even if the body of a destructor is empty, it doesn’t mean that this destructor won’t execute any code. The C++ compiler augments the destructor with calls to destructors for bases and non-static data members. For more information on destructor augmentation and other low-level C++ details I recommend the “Inside the C++ Object Model” book by Stanley L. Lippman.

Note also that an explicit empty inline definition of a destructor should be essentially equivalent to an implicitly-defined one. This is true from the language point of view with a few reservations (e.g., such a class can no longer be a POD type). In practice, however, some implementations in some circumstances may choose not to inline an explicitly-defined destructor or expression involving such a destructor because an empty inline destructor is still “more” than the trivial destructor. And this makes an implicitly-declared trivial destructor a much better option from the performance and footprint point of view. As a result, if we are providing an empty destructor, it only makes sense to define it as non-inline. And the only reason for doing this is to make the destructor non-inline. Now, the question is, are there any good reasons for making an empty destructor non-inline?

Class with non-virtual destructor

Let’s start by considering a class with a non-virtual destructor. While there are a few special cases which are discussed below, generally, there are no good reasons to prefer a non-inline empty destructor to the synthesized one. If a class has a large number of data members (or bases) that all have non-trivial destructors, then, as mentioned above, the augmented destructor may contain quite a few calls. However, chances are good a C++ compiler will not actually inline calls to such a destructor due to its complexity. In this case, object files corresponding to translation units that call such a destructor may end up containing multiple instances of the destructor. While they will be weeded out at the link stage, the need to instantiate the same destructor multiple times adds to the compilation time. However, in most cases, I believe this will be negligible.

The same reasoning applies to class templates with non-virtual destructors.

Class with virtual destructor

If a destructor is made virtual, then we also get an entry for it in the virtual function table (vtbl from now on for short). And this entry needs to be populated with a pointer to the destructor. As a result, even if the destructor is inline, there will be a non-inline instantiation of this destructor.

At first this may sound like a good reason to provide our own non-inline empty implementation. But, on closer inspection, there doesn’t seem to be any benefit in doing this. In either case there will be a non-inline version of the destructor for the vtbl. And when the compiler is able to call the destructor without involving the vtbl (i.e., when it knows that the object’s static and dynamic types are the same), then we can apply exactly the same reasoning as above.

Another thing that we may want to consider here is the instantiation of the vtbl itself. Normally, the vtbl for a class is generated when compiling a translation unit containing the first non-inline member function definition of this class. In this case we end up with a single vtbl instantiation and no resources are wasted. However, if a class only has inline functions (including our compiler-synthesized destructor), then the compiler has to fall to a less optimal method by instantiating the vtbl in every translation unit that creates an instance of an object and then weeding our duplicates at the link stage. If this proves to be expensive (e.g., you have hundreds of translation units using this class), then you may want to define an empty non-inline destructor just to anchor the vtbl.

Note also that in C++98 it is not possible to declare a destructor virtual but let the compiler synthesize the implementation (this is possible in C++11 as we will see shortly). So here we have to define an empty destructor and the question is whether to make it inline or not. Based on the above analysis I would say make it inline for consistency with the derived classes which will have inline, compiler-synthesized destructors. That is:

class base
{
public:
  virtual ~base () {}
 
  ...
};

Class template with virtual destructor

The same analysis applies here except now we always have potentially multiple vtbl instantiations, regardless of whether our destructor is inline or not. And this gives us one less reason to provide one ourselves.

To summarize, in all three cases my recommendation is to let the compiler define an inline destructor for you. Let’s now consider a few special cases where we have to make the destructor non-inline.

Special cases

There are two such special but fairly common cases that I am aware of. If you know of others, I would appreciate it if you mentioned them in the comments.

The first case can be generally described as needing extra information to be able to correctly destroy data members of a class. The most prominent example of this case is the pimpl idiom. When implemented using a smart pointer and a hidden “impl” class, the inline destructor won’t work because it needs to “see” the “impl” class declaration. Here is an example:

// object.hxx
//
class object
{
public:
  object ();
 
  // ~object () {} // error: impl is incomplete
  ~object ();
 
  ...
 
private:
  class impl;
  std::unique_ptr<impl> impl_;
};
 
// object.cxx
//
class object::impl
{
  ...
};
 
object::
object ()
  : impl_ (new impl)
{
}
 
object::
~object ()
{
  // ok: impl is complete
}

Another example of this case is Windows-specific. Here, if your object is part of a DLL interface and the DLL and executable use different runtime libraries, then you will run into trouble if your object allocates dynamic memory using the DLL runtime (e.g., in a non-inline constructor) but frees it using the executable runtime (e.g., in an inline destructor). By defining the destructor non-inline, we can make sure that the memory is allocated and freed using the same runtime.

The second case has to do with interface stability. Switching from a compiler-provided inline definition to a user-provided non-inline one changes the binary interface of a class. So if you need a binary-compatible interface, then it may make sense to define a non-inline empty destructor if there is a possibility that some functionality may have to be added to it later.

C++11 improvements

C++11 provides us with the ability to control inline-ness and virtual-ness of the compiler-defined destructor using the defaulted functions mechanism. Here is how we can declare a virtual destructor with the default implementation:

class base
{
public:
  virtual ~base () = default; // inline
 
  ...
};

To make the default implementation non-inline we have to move the definition of the destructor out of the class, for example:

// derived.hxx
//
class derived: public base
{
public:
  virtual ~derived ();
 
  ...
};
 
// derived.cxx
//
derived::~derived () = default;

Note that making a default implementation virtual or non-inline also makes it non-trivial.

Checklist

To be able to quickly decide whether a class needs an empty non-inline destructor definition I condensed the above analysis into a short checklist. When designing a class interface, ask yourself the following three questions:

  1. Do you need to anchor the vtbl (doesn’t apply to class templates)?
  2. Does proper destruction of data members require additional declarations or functionality that is not available in the class interface? Does the destruction need to be done consistently with construction (e.g., using the same runtime)?
  3. Do you need to define a stable interface and chances are that later you may have to add some functionality to the destructor?

If the answers to all these questions are “No”, then let the compiler provide the default implementation of the destructor.

C++11 support in ODB

Tuesday, March 27th, 2012

One of the major new features in the upcoming ODB 2.0.0 release is support for C++11. In this post I would like to show what is now possible when using ODB in the C++11 mode. Towards the end I will also mention some of the interesting implementation-related issues that we encountered. This would be of interest to anyone who is working on general-purpose C++ libraries or tools that have to be compatible with multiple C++ compilers as well as support both C++98 and C++11 from the same codebase.

In case you are not familiar with ODB, it is an object-relational mapping (ORM) system for C++. It allows you to persist C++ objects to a relational database without having to deal with tables, columns, or SQL, and manually writing any of the mapping code.

While the 2.0.0 release is still a few weeks out, if you would like to give the new C++11 support a try, you can use the 1.9.0.a1 pre-release.

While one could use most of the core C++11 language features with ODB even before 2.0.0, what was lacking is the integration with the new C++11 standard library components, specifically smart pointers and containers. By default, ODB still compiles in the C++98 mode, however, it is now possible to switch to the C++11 mode using the --std c++11 command line option (this is similar to GCC’s --std=c++0x). As you may remember, ODB uses GCC as a C++ compiler frontend which means ODB has arguably the best C++11 feature coverage available, especially now with the release of GCC 4.7.

Let’s start our examination of the C++11 standard library integration with smart pointers. New in C++11 are std::unique_ptr and std::shared_ptr/weak_ptr. Both of these smart pointers can now be used as object pointers:

#include <memory>
 
class employer;
 
#pragma db object pointer(std::unique_ptr)
class employee
{
  ...
 
  std::shared_ptr<employer> employer_;
};
 
#pragma db object pointer(std::shared_ptr)
class employer
{
  ...
};

ODB now also provides lazy variants for these smart pointers: odb::lazy_unique_ptr, odb::lazy_shared_ptr, and odb::lazy_weak_ptr. Here is an example:

#include <memory>
#include <vector>
 
#include <odb/lazy-ptr.hxx>
 
class employer;
 
#pragma db object pointer(std::shared_ptr)
class employee
{
  ...
 
  std::shared_ptr<employer> employer_;
};
 
#pragma db object pointer(std::shared_ptr)
class employer
{
  ...
 
  #pragma db inverse(employer_)
  std::vector<odb::lazy_weak_ptr<employee>> employees_;
};

Besides as object pointers, unique_ptr and shared_ptr/weak_ptr can also be used in data members. For example:

#include <memory>
#include <vector>
 
#pragma db object
class person
{
  ...
 
  #pragma db type("BLOB") null
  std::unique_ptr<std::vector<char>> public_key_;
};

It is unfortunate that boost::optional didn’t make it to C++11 as it would be ideal to handle the NULL semantics (boost::optional is supported by the Boost profile). The good news is that it seems there are plans to submit an std::optional proposal for TR2.

The newly supported containers are: std::array, std::forward_list, and the unordered containers. Here is an example of using std::unordered_set:

#include <string>
#include <unordered_set>
 
#pragma db object
class person
{
  ...
 
  std::unordered_set<std::string> emails_;
};

One C++11 language feature that comes really handy when dealing with query results is the range-based for-loop:

typedef odb::query<employee> query;
 
transaction t (db->begin ());
 
auto r (db->query<employee> (query::first == "John"));
 
for (employee& e: r)
  cout << e.first () << ' ' << e.last () << endl;
 
t.commit ();

So far we have tested C++11 support with various versions of GCC as well as VC++ 10 (we will also test with Clang before the final release). In fact, all the tests in our test suite build and run without any issues in the C++11 mode with these two compilers. ODB also comes with an example, called c++11, that shows support for some of the C++11 features discussed above.

These are the user-visible features when it comes to C++11 support and they are nice and neat. For those interested, here are some not so neat implementation details that I think other library authors will have to deal with if they decide to support C++11.

The first issue that we had to address is simultaneous support for C++98 and C++11. In our case, supporting both from the same codebase was not that difficult (though more on that shortly). We just had to add a number of #ifdef ODB_CXX11.

What we only realized later was that to make C++11 support practical we also had to support both from the same installation. To understand why, consider what happens when a library is packaged, say, for Ubuntu or Fedora. A single library is built and a single set of headers is packaged. To be at all usable, these packages cannot be C++98 or C++11. They have to support both at the same time. It is probably possible to have two versions of the library and ask the user to link to the correct one depending on which C++ standard they are using. But you will inevitably run into tooling limitations (e.g., pkg-config doesn’t have the --std c++11 option). The situation with headers are even worse, unless your users are prepared to pass a specific -I option depending on which C++ standard they are using.

The conclusion that we came to is this: if you want your library to be usable once installed in both C++98 and C++11 modes in a canonical way (i.e., without having to specify extra -I options, defines, or different libraries to link), then the C++11 support has to be header-only.

This has some interesting implications. For example, initially, we used an autoconf test to detect whether we are in the C++11 mode and write the appropriate value to config.h. This had to be scraped and we now use a more convoluted and less robust way of detecting the C++ standard using pre-defined compiler macros such as __cplusplus and __GXX_EXPERIMENTAL_CXX0X__. The other limitation of this decision is that all “extra” C++11 functions, such as move constructors, etc., have to be inline or templates. While these restrictions sound constraining, so far we didn’t have any serious issues maintaining C++11 support header-only. Things fitted quite naturally into this model but that, of course, may change in the future.

The other issue that we had to deal with is the different level of C++11 support provided by different compiler implementations. While GCC is more or less the gold standard in this regard, VC++ 10 lacked quite a few features that we needed, specifically, deleted functions, explicit conversion operators, and default function template arguments. As a result, we had to introduce additional macros that indicate which C++11 features are available. This felt like early C++98 days all over again. Interestingly, none of the above mentioned three features will be supported in the upcoming VC++ 11. In fact, if you look at the VC++ C++11 support table, it is quite clear that Microsoft is concentrating on the user-facing features, like the range-based for-loop. This means there will probably be some grief for some time for library writers.