The tuple is one of the most versatile and powerful data structures in C++, allowing you to combine related data without declaring a full-blown class – for example, x-y coordinates, name-address records, or the properties of a GUI element. It avoids performance overhead and eliminates boilerplate conversions while still being strictly typed. Almost too good to be true!

Interestingly, tuples are not inherently built into the C++ language. They are part of the C++ standard library, where the <tuple> header employs every trick in C++’s template programming book to achieve a flexible, efficient and type-safe implementation. We recently had to address the complexities that these introduce when testing C++ tuples for functional safety.

However, with the recent introduction of support for the tuple header in SuperGuard, this is no longer a concern. SuperGuard handles everything for you. 

If you’re interested in how some of these complexities arise, here’s an example.

Being statically sized and typed, most of a tuple’s functionality lies in its construction. C++ allows tuples to be created using many different constructors. For example:

  • from the tuple’s types
  • via copy/move of a similar tuple
  • from a pair

But also from tuples, types, and pairs whose types can be used to construct those of the tuple. 

Very convenient for the user! But it is a lot less straightforward to resolve these constructor calls.

Suppose a user-type A can be implicitly converted from a tuple<A>. If we construct a tuple<A> from a tuple<A>, it could be considered construction via copy; from the A type; from a type convertible to A; or from a tuple of such a type. If the chosen constructor is ambiguous, the program is ill-formed.

To resolve matters, the elaborate overload resolution rules from C++ kick in. In addition, tuple constructors conditionally disable themselves from participating in overload resolution. In order to test these constructors in SuperGuard, we must therefore keep in mind C++’s overload resolution rules and methodically enable and disable them as needed. Tests for one constructor would quickly become inconclusive if another constructor acts as a backup or even takes priority.

Constructors may distinguish between a case where inner types are constructible or convertible from another type: the distinction between A a(b) being valid and A a = b. Although at a first glance these two statements seem functionally identical, which they are for most conventional classes, the difference matters to the tuple. In our tests, we disentangle these two concepts, allowing us to configure each test to target the exact constructor we mean to.

The above is just the tip of the iceberg. There are many more details to consider for functional safety when just constructing tuples, let alone for the <tuple> header as a whole. In SuperGuard every case is thoroughly considered, documented, and tested. SuperGuard takes care of all these details for you, giving you peace of mind when using the <tuple> header in a safety-critical project.

 

Max Blankestijn, Software Engineer


Subscribe to our monthly blog!