The tupple library

A tuple is a collection of an arbitrary, but fixed number of elements. The type of each element is arbitrary, but fixed, too - "fixed" here means known at compile-time. In other words, tuples serve as a generalization of std::pair for n (instead of two) values.

The tupple library provides tuples much as the Boost.Tuple library by Jaakko Järvi. In addition, specialized classes for tuples of iterators and tuples of containers are defined.

The header files of the tupple library are generated by the C preprocessor with the help of the Boost.Preprocessor library (therefore the name tupple). Hence, it might also serve as a demonstration of the use and power of the Boost.Preprocessor library.

Table of Contents

  1. The basic tuple type
  2. Extension to iterators and containers
  3. Differences between Tupple and Boost.tuple
  4. Portability
  5. Design rationale
  6. Performance
  7. Mini-FAQ
  8. Acknowledgements

The basic tuple type

Tuple construction

Tupples provide the following constructors:
  1. Empty constructor: tuple<int,double>()
  2. Element-wise constructor: tuple<int,double>(42,3.1415)
  3. Copy construction:
      tuple<int,double> u(42,3.1415);
      tuple<long,float> v(u);
      

    Note that corresponding element types are not required to be equal; it is just necessary for the former element type to be convertible to the later one (for instance, due to a constructor).

  4. Construction out of head and tail:
        tuple<int,double> a(42,3.1415);
        tuple<string,int,double> b("foo",a);
      
The library additionally provides a type generator n_fold_tuple which allows construction of tuples where all elements are of the same type. Thus, the statement
  n_fold_tuple<int,5>::type
is esentially the same as writting
  tuple<int,int,int,int,int>

Element access

The elements of a tuple may be accessed by calling either
getN(t);
or calling the member function
t.getN();
where N is between 0 and size of tuple-1. Depending on whether the tuple is const or non-const, the return type is either a const or a non-const reference to element type. In addition there are two functions to access the first element and the tuple containing all but the first, respectively.
  t.head();
is essentially the same as calling t.get0(). Each tuple type defines its own tail_type to be the tuple consisting of all but the first elements, i.e. tuple<T0,T1,T2,T3>::tail_type is tuple<T1,T2,T3>. Accordingly, the statement
     t.tail();
returns a tuple of t's tail_type, containing all but the first element.

Assignment and swapping

Tuples can be assigned to other tuples if each element type is assignable. Implicit conversion is done as with the copy constructor.
tuple<int,double> u(42,3.1415);
tuple<long,float> v;
v = u;

make_tuple and tie

The make_tuple function is intended to ease the creation of tuples (analogous to the make_pair function):
  tuple<int,double,foo> u = make_tuple( 42,3.14,foo("bar") );
The tie function comes in handy if one wants to extract data back from a tuple into individual variables.
  int i; foo f;
  tie( i, ignore, f ) = u;
    // now i == 42 and f == foo("bar").

Relational operators

Often, one needs to test tuples for equality and inequality. Additionally, testing for less than, greater than etc. w.r.t. lexicographical ordering is useful sometimes:
  tuple<int,double,foo> u( 42,2.78,foo("bar") );
  tuple<long,float,foo> w( 42,3.14,foo("bar") );

  if( u == w ) // element-wise equality
    // do something

  if( u < w ) // lexicographical ordering
    // do something different

Helpers for functions

Say there is a function that expects three separate arguments; however, we would like to pass one 3-tuple to it. For such situations, a helper function which maps one calling form to the other comes in handy:
  bool is_fooable( int,double,foo ); // a function taking three args
  // ...

  tuple<int,double,foo> u( 42,2.78,foo("bar") ); // the argument

  pointer_to_function<bool,int,double,foo> fooable( is_fooable );

  if( fooable(u) ) // calls is_fooable( get0(u),get1(u),get2(u) )

Extension to iterators and containers

The motivation to reimplement the tuple type was that I wanted to group containers and iterators together in a tuple in the same manner as ``plain old data types''. This is necessary, for instance, to generalize the implementation of a zip_view, see VTL. The intended meaning is straight-forward: A tuple of, say, three containers (always in the sense of STL containers such as std::vector, std::list etc.) containing ints, doubles,and foos, respectively, should be ``the same'' as a container which contains tuple<int,double,foo>s as elements.

Tuples of iterators

  vector<int> u;
  vector<double> v;
  vector<foo> w;

  // fill u, v, and w with data

  typedef container_tuple< vector<int>,vector<double>,vector<foo> >
          my_vectors;

  my_vectors t( u,v,w );
  my_vectors::iterator it;

  for( it = t.begin(); it != t.end(); ++it )
  {
    // (*it) is of type tuple<int,double,foo>:
    int i = get0( *it );
    it->get1() = 3.14 * i;
  }

Tuples of containers

  // u, v, and w as above
  my_vectors t( u,v,w );

  tuple<int,int,int> index( 1,4,3 );
  t[ index ] = make_tuple( 42,3.14,foo("bar") );

Differences between Tupple and Boost.tuple

Apart from additional functionality, Tupple and Boost.Tuple are almost identical w.r.t. the interface. One small difference:

Portability

The tupple library relies heavily on partial template instantiation; thus, a relatively recent compiler is necessary. So far, it is tested to work with with gcc 2.95.3-6 (mingw special) and gcc-3.2 (cygwin).

Known issues with MSVC++

In case that there is no partial template specialization, a special version of the tupple library is used.

Design rationale

Of course it might have been possible to add operators like operator++, operator--etc. to each tuple class. I decided against this option. Instead, two classes, iterator_tuple and container_tuple representing tuples of iterators and (STL) containers are derived from tuple.

The process of creating tupple.hpp

One of the major characteristics of the tupple library is that its source code is generated by the C preprocessor with the help of the Boost.Preprocessor library. This is done in two steps:
  1. gcc is called with the proper options (-E -C -P) to run the preprocessor only, and to save the preprocessed output to a file. The result is valid code, but in barely readable messy form.
  2. indent reformats the preprocessed source code, and puts the result in the correct directory.
For further details, have a look at this Technical Report.

Performance

All in all, the Tupple library matches Boost.Tuple in performance. Various tests did show a speedup of about 2-5% (which is as good as nothing). Compile time should also be shorter, but since this is even harder to measure, I can not provide any reliable data on that yet.

Mini-FAQ

Q: When compiling under Linux, I get messages like "Invalid token in expression" or "stray '\' in program".
A: Usually, this is caused by a backslash '\' within a macro definition, followed by a wrong (i.e. DOS-like) end-of-line character. I experienced this problem not only with tupple.hpp, but also with files from boost/config, such as suffix.hpp, posix_features.hpp etc. Saving the files as "non-DOS" fixed the problem.

Q: The test program fails.
A: Test statements like

  int a;
  BOOST_TEST(a == int());
pass for some compilers, and fail for others. It is surprising to see that even closely related compilers such as gcc-2.95.3 cygwin (pass) and gcc-2.95.2 under Linux (fail) show different behaviour. Besides, I'm not even sure whether this should pass or fail.

Q: I get compiler errors when using operator!=()
A: Some distributions of the STL define operator!=() (such as SGI's STL in stl_relop.h), others don't (such as STLPort). If you experience troubles, you should #define TUPPLE_SKIP_NOT_EQUAL before including tupple.hpp.

Acknowledgements

Some code of the Boost.tuple library was re-used with the kind permission of Jaakko Järvi.

Last modified 17 Dez 2002.

Copyright © Roland Richter 2002. Permission to copy, use, modify, sell and distribute this software is granted provided this copyright notice appears in all copies. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.