Since my last two posts (here and here) are about how … might unpack and generate code if it were very smart, I thought I’d bring up a more difficult case. How do you build templated constructors inside the variadic template tuple class?

Remember, this is all fantasy code. The … operator will not actually be able to do any of this. Also remember this problem can be solved by defining tuple using a recursive first/rest typelist scheme (see the current Boost implementation).

At first glance it might appear that it’s easy to define a constructor. Just do this.

I think the double … expansion in the parameter list works. I’m pretty sure you only specify … immediately before the variable to be packed, and that all the already-packed variables in the expression will be unpacked at the same time. So you don’t specify … again after TYPEs( ).

In any case, this still needs improvement. This requires that all TYPEs have a copy constructor even if they are going to be default constructed. We really want a bunch of constructors, taking zero, one, two, etc arguments.

Yikes, that’s not C++, and we’re not in Kansas anymore. In this fantasy code for… is a meta keyword, interpreted by the compiler and producing a series of constructors. And unpack…(N) only unpacks the first N items in the packed variable TYPEs, and not the entire list.

Instead of for… in the above example we could use plain old for and say the compiler should run code that is not in a code-defining context. But we’d still need to quote or emit the tokens inside the loop above. Something like this.

This implementation is still incomplete however. The constructor methods themselves should be templated so you can construct tuples from values with convertible types. With that in mind, here is the last implementation.

This introduces the fantasy generate...(N) meta function to generate a template parameter list of N typenames. This also unpacks values and args at the same time (and in parallel) even though values is packed from TYPEs while args is packed from ARG_TYPES. And args usually packs fewer names than values.

This kind of imperative code generation is sometimes easier to create and understand than the declarative templates C++ currently provides. But it also adds more syntax and keywords to C++, which is plenty complicated already. And a declarative solution that isn't as restrictive as C++ templates may be a better than this imperative suggestion. So I'm not advocating any of this stuff. I'm just thinking out loud, writing it down to see what it looks like.

Abstraction

A tuple is a lot like an old C-style POD struct. Both are heterogeneous aggregators, holding a collection of objects of various types. Neither are open ended, as the types and counts of the enclosed values are fixed at compile time.

Of course a modern C++ struct can also have methods, supertypes, static members, etc. A tuple doesn’t do any of that. It is bare and public, like struct circa 1980, before templates, when compilers were not expected to generate code.

So how are they different? Why bother with tuple when you can just use struct?

You don’t have to declare a tuple<..> type before you use it, like you do with struct. You just use tuples when you need one. They are declared on-the-fly, at the point of usage.

tuples and structs both provide a default constructor, a destructor, a copy constructor, and a copy assignment operator. tuple are much more flexible during copy however, converting compatible inside types when possible. A struct enforces its type, while a tuple is just bundling values.

A tuple also provides constructors that can explicitly set member variables. With structs you either have to write the (obvious) constructor yourself, or use compiler initialization lists when that’s possible.

Unlike tuples, struct types are (usually) named. tuples are thin wrappers around their innards. They are not modest. structs attempt more abstraction, both by being named and by using names to access members.

structs protect their identity, sometimes to a fault. You cannot declare an identical named struct twice in the same compilation unit. Identical anonymous structs are different types. This can be inconvenient.

A struct is an abstractor, a hider of information, while a tuple is just an aggregator. For tuples, structure determines type. For structs, the name determines type and provides abstraction. But sometimes you don’t want abstraction, or even a name.

Metaprogramming

You access tuple members through integer indexes (i.e. tuple_inst.get<2>()). They’re like heterogeneous arrays. Contrast that with struct member access, which is by name (i.e. struct_inst.cost_increase).

Index access is mostly an artifact of C++ templates, which can manipulate integers and types, but aren’t so good with names (although you can use enums to give names to index values). Lisp macros have finer-grained control, manipulating symbols (names) as well as constants and lists.

But index access does not feel unnatural because order is imposed anyway, in parameter lists and constructors. tuple<char,double> is not the same type as tuple<double,char>. Even so, there are occasions when it’s clearer to define an enum so you can say tuple_inst.get() instead of tuple_inst.get<3>(). But if you find yourself doing this, consider using a struct instead of a tuple.

The tuple type in Boost is built using a type list, a technique pioneered by Andrei Alexandrescu, creator of Loki and author of Modern C++ Design. A type list is a recursive set of templated types that can be aggregated into a larger type. I use a similar technique in the post Using templates to define an array class with constructors.

In the C++0X future tuple might be defined with variadic template parameters instead of a type list. The type list is just a technique, and it is not exposed in the interface. It is clever but not essential. A variadic implementation would be more straightforward.

In my last few posts about shared_ptr<T>s I’ve been using a struct called private_deleter. When you first attach a target object to a shared_ptr<T> you can also specify a deleter, which is a functor with an operator() that takes a single argument, a pointer to the target object, and deletes it.

You use the deleter, of course, to control target object deletion. Which is symmetric since you also control target creation. Without the deleter you could only create target objects with operator new to match the operator delete assumed by shared_ptr.

Since the deleter becomes part of the intermediate object (aka the “control block”), you can also use it as a way to add variables and functions without intruding on the target class. For example, you can use it to store a list of notifier functors to be triggered when the target is deleted. Or you can use it as a chunk of memory in which you construct the target object. But in this post we’re just going to use the deleter to delete.

The Boost smart pointers provide a shared pointer just for arrays, called shared_array<T>. It is similar to shared_ptr<T> except it uses operator delete[] instead of operator delete to delete the target. But shared_array<T> is not part of TR1, and it is not necessary since shared_ptr<T> supports custom deleters.

The following shows how to use a deleter so shared_ptr<T> correctly deletes arrays.

Since shared_array<T> is no longer necessary to support array deletion, I suspect it will never become part of the standard library. operator [] is not enough justification for its existence. It will languish in the Boost library, a barely supported dead-end experiment, and will never be integrated with shared_ptr and weak_ptr.

As a final note, another way to work with arrays and shared_ptrs is to use an array wrapper like the TR1 array class. Since you can allocate these objects with operator new you can rely on the default shared_ptr deleter.