526 lines
18 KiB
Plaintext
526 lines
18 KiB
Plaintext
[/
|
|
/ Copyright (c) 2001 Jaakko Järvi
|
|
/
|
|
/ Distributed under the Boost Software License, Version 1.0. (See
|
|
/ accompanying file LICENSE_1_0.txt or copy at
|
|
/ http://www.boost.org/LICENSE_1_0.txt)
|
|
/]
|
|
|
|
[library Boost.Tuple
|
|
[quickbook 1.6]
|
|
[id tuple]
|
|
[copyright 2001 Jaakko J\u00E4rvi]
|
|
[dirname tuple]
|
|
[license Distributed under the
|
|
[@http://boost.org/LICENSE_1_0.txt Boost Software License,
|
|
Version 1.0].
|
|
]
|
|
]
|
|
|
|
[include tuple_advanced_interface.qbk]
|
|
[include design_decisions_rationale.qbk]
|
|
|
|
[template simplesect[title]
|
|
[block '''<simplesect><title>'''[title]'''</title>''']]
|
|
|
|
[template endsimplesect[]
|
|
[block '''</simplesect>''']]
|
|
|
|
A tuple (or n-tuple) is a fixed size collection of elements. Pairs, triples,
|
|
quadruples etc. are tuples. In a programming language, a tuple is a data
|
|
object containing other objects as elements. These element objects may be of
|
|
different types.
|
|
|
|
Tuples are convenient in many circumstances. For instance, tuples make it easy
|
|
to define functions that return more than one value.
|
|
|
|
Some programming languages, such as ML, Python and Haskell, have built-in
|
|
tuple constructs. Unfortunately C++ does not. To compensate for this
|
|
"deficiency", the Boost Tuple Library implements a tuple construct using
|
|
templates.
|
|
|
|
[section:using_library Using the Library]
|
|
|
|
To use the library, just include:
|
|
|
|
#include "boost/tuple/tuple.hpp"
|
|
|
|
Comparison operators can be included with:
|
|
|
|
#include "boost/tuple/tuple_comparison.hpp"
|
|
|
|
To use tuple input and output operators,
|
|
|
|
#include "boost/tuple/tuple_io.hpp"
|
|
|
|
Both `tuple_io.hpp` and `tuple_comparison.hpp` include `tuple.hpp`.
|
|
|
|
All definitions are in namespace `::boost::tuples`, but the most common names
|
|
are lifted to namespace `::boost` with using declarations. These names are:
|
|
`tuple`, `make_tuple`, `tie` and `get`. Further, `ref` and `cref` are defined
|
|
directly under the `::boost` namespace.
|
|
|
|
[endsect]
|
|
|
|
[section:tuple_types Tuple Types]
|
|
|
|
A tuple type is an instantiation of the `tuple` template. The template
|
|
parameters specify the types of the tuple elements. The current version
|
|
supports tuples with 0-10 elements. If necessary, the upper limit can be
|
|
increased up to, say, a few dozen elements. The data element can be any C++
|
|
type. Note that `void` and plain function types are valid C++ types, but
|
|
objects of such types cannot exist. Hence, if a tuple type contains such types
|
|
as elements, the tuple type can exist, but not an object of that type. There
|
|
are natural limitations for element types that cannot be copied, or that are
|
|
not default constructible (see [link tuple.constructing_tuples 'Constructing tuples']
|
|
below).
|
|
|
|
For example, the following definitions are valid tuple instantiations (`A`,
|
|
`B` and `C` are some user defined classes):
|
|
|
|
tuple<int>
|
|
tuple<double&, const double&, const double, double*, const double*>
|
|
tuple<A, int(*)(char, int), B(A::*)(C&), C>
|
|
tuple<std::string, std::pair<A, B> >
|
|
tuple<A*, tuple<const A*, const B&, C>, bool, void*>
|
|
|
|
[endsect]
|
|
|
|
[section:constructing_tuples Constructing Tuples]
|
|
|
|
The tuple constructor takes the tuple elements as arguments. For an /n/-
|
|
element tuple, the constructor can be invoked with /k/ arguments, where
|
|
`0` <= /k/ <= /n/. For example:
|
|
|
|
tuple<int, double>()
|
|
tuple<int, double>(1)
|
|
tuple<int, double>(1, 3.14)
|
|
|
|
If no initial value for an element is provided, it is default initialized
|
|
(and hence must be default initializable). For example:
|
|
|
|
class X {
|
|
X();
|
|
public:
|
|
X(std::string);
|
|
};
|
|
|
|
tuple<X,X,X>() // error: no default constructor for X
|
|
tuple<X,X,X>(string("Jaba"), string("Daba"), string("Duu")) // ok
|
|
|
|
In particular, reference types do not have a default initialization:
|
|
|
|
tuple<double&>() // error: reference must be
|
|
// initialized explicitly
|
|
|
|
double d = 5;
|
|
tuple<double&>(d) // ok
|
|
|
|
tuple<double&>(d+3.14) // error: cannot initialize
|
|
// non-const reference with a temporary
|
|
|
|
tuple<const double&>(d+3.14) // ok, but dangerous:
|
|
// the element becomes a dangling reference
|
|
|
|
Using an initial value for an element that cannot be copied, is a compile time
|
|
error:
|
|
|
|
class Y {
|
|
Y(const Y&);
|
|
public:
|
|
Y();
|
|
};
|
|
|
|
char a[10];
|
|
|
|
tuple<char[10], Y>(a, Y()); // error, neither arrays nor Y can be copied
|
|
tuple<char[10], Y>(); // ok
|
|
|
|
Note particularly that the following is perfectly ok:
|
|
|
|
Y y;
|
|
tuple<char(&)[10], Y&>(a, y);
|
|
|
|
It is possible to come up with a tuple type that cannot be constructed. This
|
|
occurs if an element that cannot be initialized has a lower index than an
|
|
element that requires initialization. For example: `tuple<char[10], int&>`.
|
|
|
|
In sum, the tuple construction is semantically just a group of individual
|
|
elementary constructions.
|
|
|
|
[section:make_tuple The `make_tuple` function]
|
|
|
|
Tuples can also be constructed using the `make_tuple` (cf. `std::make_pair`)
|
|
helper functions. This makes the construction more convenient, saving the
|
|
programmer from explicitly specifying the element types:
|
|
|
|
tuple<int, int, double> add_multiply_divide(int a, int b) {
|
|
return make_tuple(a+b, a*b, double(a)/double(b));
|
|
}
|
|
|
|
By default, the element types are deduced to the plain non-reference types.
|
|
E.g.:
|
|
|
|
void foo(const A& a, B& b) {
|
|
...
|
|
make_tuple(a, b);
|
|
|
|
The `make_tuple` invocation results in a tuple of type `tuple<A, B>`.
|
|
|
|
Sometimes the plain non-reference type is not desired, e.g. if the element
|
|
type cannot be copied. Therefore, the programmer can control the type
|
|
deduction and state that a reference to const or reference to non-const type
|
|
should be used as the element type instead. This is accomplished with two
|
|
helper template functions: [@boost:/libs/core/doc/html/core/ref.html `boost::ref`]
|
|
and [@boost:/libs/core/doc/html/core/ref.html `boost::cref`]. Any argument can
|
|
be wrapped with these functions to get the desired type. The mechanism does
|
|
not compromise const correctness since a const object wrapped with ref results
|
|
in a tuple element with const reference type (see the fifth example below).
|
|
For example:
|
|
|
|
A a; B b; const A ca = a;
|
|
make_tuple(cref(a), b); // creates tuple<const A&, B>
|
|
make_tuple(ref(a), b); // creates tuple<A&, B>
|
|
make_tuple(ref(a), cref(b)); // creates tuple<A&, const B&>
|
|
make_tuple(cref(ca)); // creates tuple<const A&>
|
|
make_tuple(ref(ca)); // creates tuple<const A&>
|
|
|
|
Array arguments to `make_tuple` functions are deduced to reference to const
|
|
types by default; there is no need to wrap them with `cref`. For example:
|
|
|
|
make_tuple("Donald", "Daisy");
|
|
|
|
This creates an object of type `tuple<const char (&)[7], const char (&)[6]>`
|
|
(note that the type of a string literal is an array of const characters, not
|
|
`const char*`). However, to get `make_tuple` to create a tuple with an element
|
|
of a non-const array type one must use the `ref` wrapper.
|
|
|
|
Function pointers are deduced to the plain non-reference type, that is, to
|
|
plain function pointer. A tuple can also hold a reference to a function, but
|
|
such a tuple cannot be constructed with `make_tuple` (a const qualified
|
|
function type would result, which is illegal):
|
|
|
|
void f(int i);
|
|
...
|
|
make_tuple(&f); // tuple<void (*)(int)>
|
|
...
|
|
tuple<tuple<void (&)(int)> > a(f) // ok
|
|
make_tuple(f); // not ok
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|
|
[section:accessing_elements Accessing Tuple Elements]
|
|
|
|
Tuple elements are accessed with the expression:
|
|
|
|
t.get<N>()
|
|
|
|
or
|
|
|
|
get<N>(t)
|
|
|
|
where `t` is a tuple object and `N` is a constant integral expression
|
|
specifying the index of the element to be accessed. Depending on whether `t`
|
|
is const or not, `get` returns the `N`-th element as a reference to const or
|
|
non-const type. The index of the first element is `0` and thus `N` must be
|
|
between `0` and /k/`-1`, where /k/ is the number of elements in the tuple.
|
|
Violations of these constraints are detected at compile time. Examples:
|
|
|
|
double d = 2.7; A a;
|
|
tuple<int, double&, const A&> t(1, d, a);
|
|
const tuple<int, double&, const A&> ct = t;
|
|
...
|
|
int i = get<0>(t); i = t.get<0>(); // ok
|
|
int j = get<0>(ct); // ok
|
|
get<0>(t) = 5; // ok
|
|
get<0>(ct) = 5; // error, can't assign to const
|
|
...
|
|
double e = get<1>(t); // ok
|
|
get<1>(t) = 3.14; // ok
|
|
get<2>(t) = A(); // error, can't assign to const
|
|
A aa = get<3>(t); // error: index out of bounds
|
|
...
|
|
++get<0>(t); // ok, can be used as any variable
|
|
|
|
/[Note:/ The member `get` functions are not supported with MS Visual C++
|
|
compiler. Further, the compiler has trouble with finding the non-member `get`
|
|
functions without an explicit namespace qualifier. Hence, all `get` calls
|
|
should be qualified as `tuples::get<N>(a_tuple)` when writing code that should
|
|
compile with MSVC++ 6.0./]/
|
|
|
|
[endsect]
|
|
|
|
[section:construction_and_assignment Copy Construction and Tuple Assignment]
|
|
|
|
A tuple can be copy constructed from another tuple, provided that the element
|
|
types are element-wise copy constructible. Analogously, a tuple can be
|
|
assigned to another tuple, provided that the element types are element-wise
|
|
assignable. For example:
|
|
|
|
class A {};
|
|
class B : public A {};
|
|
struct C { C(); C(const B&); };
|
|
struct D { operator C() const; };
|
|
tuple<char, B*, B, D> t;
|
|
...
|
|
tuple<int, A*, C, C> a(t); // ok
|
|
a = t; // ok
|
|
|
|
In both cases, the conversions performed are:
|
|
|
|
* `char -> int`,
|
|
* `B* -> A*` (derived class pointer to base class pointer),
|
|
* `B -> C` (a user defined conversion), and
|
|
* `D -> C` (a user defined conversion).
|
|
|
|
Note that assignment is also defined from `std::pair` types:
|
|
|
|
tuple<float, int> a = std::make_pair(1, 'a');
|
|
|
|
[endsect]
|
|
|
|
[section:relational_operators Relational Operators]
|
|
|
|
Tuples reduce the operators `==`, `!=`, `<`, `>`, `<=` and `>=` to the
|
|
corresponding elementary operators. This means, that if any of these operators
|
|
is defined between all elements of two tuples, then the same operator is
|
|
defined between the tuples as well. The equality operators for two tuples `a`
|
|
and `b` are defined as:
|
|
|
|
* `a == b` iff for each `i`: `a`'''<subscript>i</subscript>'''` == b`'''<subscript>i</subscript>'''
|
|
* `a != b` iff exists `i`: `a`'''<subscript>i</subscript>'''` != b`'''<subscript>i</subscript>'''
|
|
|
|
The operators `<`, `>`, `<=` and `>=` implement a lexicographical ordering.
|
|
|
|
Note that an attempt to compare two tuples of different lengths results in a
|
|
compile time error. Also, the comparison operators are /"short-circuited"/:
|
|
elementary comparisons start from the first elements and are performed only
|
|
until the result is clear.
|
|
|
|
Examples:
|
|
|
|
tuple<std::string, int, A> t1(std::string("same?"), 2, A());
|
|
tuple<std::string, long, A> t2(std::string("same?"), 2, A());
|
|
tuple<std::string, long, A> t3(std::string("different"), 3, A());
|
|
|
|
bool operator==(A, A) { std::cout << "All the same to me..."; return true; }
|
|
|
|
t1 == t2; // true
|
|
t1 == t3; // false, does not print "All the..."
|
|
|
|
[endsect]
|
|
|
|
[section:tiers Tiers]
|
|
|
|
/Tiers/ are tuples, where all elements are of non-const reference types. They
|
|
are constructed with a call to the `tie` function template (cf. `make_tuple`):
|
|
|
|
int i; char c; double d;
|
|
...
|
|
tie(i, c, a);
|
|
|
|
The above `tie` function creates a tuple of type `tuple<int&, char&, double&>`.
|
|
The same result could be achieved with the call `make_tuple(ref(i), ref(c), ref(a))`.
|
|
|
|
A tuple that contains non-const references as elements can be used to 'unpack'
|
|
another tuple into variables. E.g.:
|
|
|
|
int i; char c; double d;
|
|
tie(i, c, d) = make_tuple(1,'a', 5.5);
|
|
std::cout << i << " " << c << " " << d;
|
|
|
|
This code prints `1 a 5.5` to the standard output stream. A tuple unpacking
|
|
operation like this is found for example in ML and Python. It is convenient
|
|
when calling functions which return tuples.
|
|
|
|
The tying mechanism works with `std::pair` templates as well:
|
|
|
|
int i; char c;
|
|
tie(i, c) = std::make_pair(1, 'a');
|
|
|
|
[section Ignore]
|
|
|
|
There is also an object called `ignore` which allows you to ignore an element
|
|
assigned by a tuple. The idea is that a function may return a tuple, only part
|
|
of which you are interested in. For example (note, that ignore is under the
|
|
`tuples` subnamespace):
|
|
|
|
char c;
|
|
tie(tuples::ignore, c) = std::make_pair(1, 'a');
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|
|
[section:streaming Streaming]
|
|
|
|
The global `operator<<` has been overloaded for `std::ostream` such that
|
|
tuples are output by recursively calling `operator<<` for each element.
|
|
|
|
Analogously, the global `operator>>` has been overloaded to extract tuples
|
|
from `std::istream` by recursively calling `operator>>` for each element.
|
|
|
|
The default delimiter between the elements is space, and the tuple is enclosed
|
|
in parenthesis. For Example:
|
|
|
|
tuple<float, int, std::string> a(1.0f, 2, std::string("Howdy folks!");
|
|
|
|
cout << a;
|
|
|
|
outputs the tuple as: `(1.0 2 Howdy folks!)`
|
|
|
|
The library defines three manipulators for changing the default behavior:
|
|
|
|
* `set_open(char)` defines the character that is output before the first element.
|
|
* `set_close(char)` defines the character that is output after the last element.
|
|
* `set_delimiter(char)` defines the delimiter character between elements.
|
|
|
|
Note, that these manipulators are defined in the tuples subnamespace. For
|
|
example:
|
|
|
|
cout << tuples::set_open('[') << tuples::set_close(']') << tuples::set_delimiter(',') << a;
|
|
|
|
outputs the same tuple `a` as: `[1.0,2,Howdy folks!]`
|
|
|
|
The same manipulators work with `operator>>` and `istream` as well. Suppose
|
|
the `cin` stream contains the following data:
|
|
|
|
(1 2 3) [4:5]
|
|
|
|
The code:
|
|
|
|
tuple<int, int, int> i;
|
|
tuple<int, int> j;
|
|
|
|
cin >> i;
|
|
cin >> tuples::set_open('[') >> tuples::set_close(']') >> tuples::set_delimiter(':');
|
|
cin >> j;
|
|
|
|
reads the data into the tuples `i` and `j`.
|
|
|
|
Note that extracting tuples with `std::string` or C-style string elements does
|
|
not generally work, since the streamed tuple representation may not be
|
|
unambiguously parseable.
|
|
|
|
[endsect]
|
|
|
|
[section:performance Performance]
|
|
|
|
All tuple access and construction functions are small inlined one-liners.
|
|
Therefore, a decent compiler can eliminate any extra cost of using tuples
|
|
compared to using hand-written tuple like classes. Particularly, with a decent
|
|
compiler there is no performance difference between this code:
|
|
|
|
class hand_made_tuple {
|
|
A a; B b; C c;
|
|
public:
|
|
hand_made_tuple(const A& aa, const B& bb, const C& cc)
|
|
: a(aa), b(bb), c(cc) {};
|
|
A& getA() { return a; };
|
|
B& getB() { return b; };
|
|
C& getC() { return c; };
|
|
};
|
|
|
|
hand_made_tuple hmt(A(), B(), C());
|
|
hmt.getA(); hmt.getB(); hmt.getC();
|
|
|
|
and this code:
|
|
|
|
tuple<A, B, C> t(A(), B(), C());
|
|
t.get<0>(); t.get<1>(); t.get<2>();
|
|
|
|
Note, that there are widely used compilers (e.g. bcc 5.5.1) which fail to
|
|
optimize this kind of tuple usage.
|
|
|
|
Depending on the optimizing ability of the compiler, the tier mechanism may
|
|
have a small performance penalty compared to using non-const reference
|
|
parameters as a mechanism for returning multiple values from a function. For
|
|
example, suppose that the following functions `f1` and `f2` have equivalent
|
|
functionalities:
|
|
|
|
void f1(int&, double&);
|
|
tuple<int, double> f2();
|
|
|
|
Then, the call #1 may be slightly faster than #2 in the code below:
|
|
|
|
int i; double d;
|
|
...
|
|
f1(i,d); // #1
|
|
tie(i,d) = f2(); // #2
|
|
|
|
See [[link publ_1 1], [link publ_2 2]] for more in-depth discussions about
|
|
efficiency.
|
|
|
|
[section Effect on Compile Time]
|
|
|
|
Compiling tuples can be slow due to the excessive amount of template
|
|
instantiations. Depending on the compiler and the tuple length, it may be more
|
|
than 10 times slower to compile a tuple construct, compared to compiling an
|
|
equivalent explicitly written class, such as the `hand_made_tuple` class above.
|
|
However, as a realistic program is likely to contain a lot of code in addition
|
|
to tuple definitions, the difference is probably unnoticeable. Compile time
|
|
increases between 5 and 10 percent were measured for programs which used tuples
|
|
very frequently. With the same test programs, memory consumption of compiling
|
|
increased between 22% to 27%. See [[link publ_1 1], [link publ_2 2]] for
|
|
details.
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|
|
[section:portability Portability]
|
|
|
|
The library code is(?) standard C++ and thus the library works with a standard
|
|
conforming compiler. Below is a list of compilers and known problems with each
|
|
compiler:
|
|
|
|
[table
|
|
[[Compiler] [Problems]]
|
|
[[gcc 2.95] [-]]
|
|
[[edg 2.44] [-]]
|
|
[[Borland 5.5] [Can't use function pointers or member pointers as
|
|
tuple elements]]
|
|
[[Metrowerks 6.2] [Can't use `ref` and `cref` wrappers]]
|
|
[[MS Visual C++] [No reference elements (`tie` still works). Can't use
|
|
`ref` and `cref` wrappers]]
|
|
]
|
|
|
|
[endsect]
|
|
|
|
[section:more_details More Details]
|
|
|
|
[link tuple_advanced_interface Advanced features] (describes some metafunctions etc.).
|
|
|
|
[link design_decisions_rationale Rationale behind some design/implementation decisions].
|
|
|
|
[endsect]
|
|
|
|
[section:thanks Acknowledgements]
|
|
|
|
Gary Powell has been an indispensable helping hand. In particular, stream
|
|
manipulators for tuples were his idea. Doug Gregor came up with a working
|
|
version for MSVC, David Abrahams found a way to get rid of most of the
|
|
restrictions for compilers not supporting partial specialization. Thanks to
|
|
Jeremy Siek, William Kempf and Jens Maurer for their help and suggestions. The
|
|
comments by Vesa Karvonen, John Max Skaller, Ed Brey, Beman Dawes, David
|
|
Abrahams and Hartmut Kaiser helped to improve the library. The idea for the
|
|
`tie` mechanism came from an old usenet article by Ian McCulloch, where he
|
|
proposed something similar for `std::pair`s.
|
|
|
|
[endsect]
|
|
|
|
[section:references References]
|
|
|
|
[#publ_1]
|
|
[1] J\u00E4rvi J.: /Tuples and multiple return values in C++/, TUCS Technical Report No 249, 1999.
|
|
|
|
[#publ_2]
|
|
[2] J\u00E4rvi J.: /ML-Style Tuple Assignment in Standard C++ - Extending the Multiple Return Value Formalism/, TUCS Technical Report No 267, 1999.
|
|
|
|
[#publ_3]
|
|
[3] J\u00E4rvi J.: /Tuple Types and Multiple Return Values/, C/C++ Users Journal, August 2001.
|
|
|
|
[endsect]
|