For example, you can get the length of a tuple the number of elements, retrieve the type of an element, and use the null_type tuple sentinel to terminate recursive template instantiation
Trang 1int main() {
boost::tuple<int,double> tup1;
boost::tuple<long,long,long> tup2;
std::cout << "Enter an int and a double as (1 2.3):\n";
std::cin >> tup1;
std::cout << "Enter three ints as |1.2.3|:\n";
std::cin >> boost::tuples::set_open('|') >>
boost::tuples::set_close('|') >>
boost::tuples::set_delimiter('.') >> tup2;
std::cout << "Here they are:\n"
<< tup1 << '\n'
<< boost::tuples::set_open('\"') <<
boost::tuples::set_close('\"') <<
boost::tuples::set_delimiter('-');
std::cout << tup2 << '\n';
}
The previous example shows how to use the streaming operators together with tuples The default delimiters for tuples are ( (left parenthesis) as opening delimiter, ) (right parenthesis) for the closing delimiter, and a space for delimiting tuple element values This implies that to get our program working correctly, we need to give the program input like(12 54.1) and |4.5.3| Here's a sample run
Enter an int and a double as (1 2.3):
(12 54.1)
Enter three ints as |1.2.3|:
|4.5.3|
Here they are:
(12 54.1)
"4-5-3"
The support for streaming is convenient and, with the support of the delimiter manipulators, it's easy to make streaming compatible even with legacy code that has been updated to use tuples
Finding Out More About Tuples
There are more facilities for tuples than those we've already seen These more advanced features are vital for creating generic constructs that work with tuples
Trang 2For example, you can get the length of a tuple (the number of elements), retrieve the type of an element, and use the null_type tuple sentinel to terminate recursive template instantiations
It's not possible to iterate over the elements of a tuple with a for loop, because get requires a constant integral expression However, using a template
metaprogram, we can print all the elements of a tuple
#include <iostream>
#include <string>
#include "boost/tuple/tuple.hpp"
template <typename Tuple,int Index> struct print_helper {
static void print(const Tuple& t) {
std::cout << boost::tuples::get<Index>(t) << '\n';
print_helper<Tuple,Index-1>::print(t);
}
};
template<typename Tuple> struct print_helper<Tuple,0> {
static void print(const Tuple& t) {
std::cout << boost::tuples::get<0>(t) << '\n';
}
};
template <typename Tuple> void print_all(const Tuple& t) {
print_helper<
Tuple,boost::tuples::length<Tuple>::value-1>::print(t);
}
int main() {
boost::tuple<int,std::string,double>
tup(42,"A four and a two",42.424242);
print_all(tup);
}
In the example, a helper class template, print_helper, is a metaprogram that visits all indices of a tuple, printing the element for each index The partial
specialization terminates the template recursion The function print_all
supplies the length of its tuple parameter, plus the tuple to a print_helper constructor The length of the tuple is retrieved like this:
boost::tuples::length<Tuple>::value
Trang 3This is a constant integral expression, which means it can be passed as the second template argument for print_helper However, there's a caveat to our solution, which becomes clear when we see the output from running the program
42.4242
A four and a two
42
We're printing the elements in reverse order! Although this could be considered a feature in some situations (he says slyly), it's certainly not the intention here The problem is that print_helper prints the value of the
boost::tuples::length<Tuple>::value-1 element first, then the value of the previous element, and so on, until the specialization prints the first element's value Rather than using the first element as the special case and starting with the last element, we need to start with the first element and use the last
element as the special case How is that possible? The solution becomes apparent after you know that tuples are terminated with a special type,
boost::tuples:: null_type We can always be certain that the last type
in a tuple is null_type, which also means that our solution involves a
specialization or function overload for null_type
The remaining issue is getting the first element's value followed by the next, and so
on, and then stopping at the end of the list tuples provide the member functions get_head and get_tail to access the elements in them As its name suggests, get_head returns the head of the sequence of valuesthat is, the first element's value get_tail returns a tuple with all but the first value in the tuple That leads to the following solution for print_all
void print_all(const boost::tuples::null_type&) {}
template <typename Tuple> void print_all(const Tuple& t) {
std::cout << t.get_head() << '\n';
print_all(t.get_tail());
}
This solution is shorter than the original, and it prints the element values in the correct order Each time the function template print_all executes, it prints one element from the beginning of the tuple and then recurses with a tuple of all but the first value in t When there are no more values in the tuple, the tail is of
Trang 4type null_type, the overloaded function print_all is called, and the
recursion terminates
It can be useful to know the type of a particular element such as when declaring variables in generic code that are initialized from tuple elements Consider a function that returns the sum of the first two elements of a tuple, with the
additional requirement that the return type must correspond to the largest type (for example, with regards to range of integral types) of the two Without somehow knowing the types of the elements, it would be impossible to create a general solution to this This is what the helper template element<N,Tuple>::type does, as the following example shows The problem we're facing not only involves calculating which element has the largest type, but declaring that type as the return value of a function This is somewhat complicated, but we can solve it using an extra level of indirection This indirection comes in the form of an additional helper template with one responsibility: to provide a typedef that defines the larger of two types The code may seem a little hairy, but it does the job
#include <iostream>
#include "boost/tuple/tuple.hpp"
#include <cassert>
template <bool B,typename Tuple> struct largest_type_helper {
typedef typename boost::tuples::element<1,Tuple>::type type;
};
template<typename Tuple> struct largest_type_helper<true,Tuple> {
typedef typename boost::tuples::element<0,Tuple>::type type;
};
template<typename Tuple> struct largest_type {
typedef typename largest_type_helper<
(sizeof(boost::tuples::element<0,Tuple>)>
sizeof(boost::tuples::element<1,Tuple>)),Tuple>::type type;
};
template <typename Tuple>
typename largest_type<Tuple>::type sum(const Tuple& t) {
typename largest_type<Tuple>::type
result=boost::tuples::get<0>(t)+
boost::tuples::get<1>(t);
return result;
}
int main() {
typedef boost::tuple<short,int,long> my_tuple;
Trang 5boost::tuples::element<0,my_tuple>::type first=14;
assert(type_id(first) == typeid(short));
boost::tuples::element<1,my_tuple>::type second=27;
assert(type_id(second) == typeid(int));
boost::tuples::element<
boost::tuples::length<my_tuple>::value-1,my_tuple>::type
last;
my_tuple t(first,second,last);
std::cout << "Type is int? " <<
(typeid(int)==typeid(largest_type<my_tuple>::type)) << '\n';
int s=sum(t);
}
If you didn't quite follow the exercise in template metaprogramming, don't
worryit's absolutely not a requirement for utilizing the Tuple library Although this type of coding takes some time getting used to, the idea is really quite simple largest_type gets the typedef from one of the two helper class templates, largest_type_helper, where one version is partially specialized on the Boolean parameter This parameter is determined by comparing the size of the two first elements of the tuple (the second template parameter) The result of this is a typedef that represents the larger of the two types Our function sum uses that type as the return value, and the rest is simply a matter of adding the two elements The rest of the example shows how to use the function sum, and also how to
declare variables with types from certain tuple elements The first two use a hardcoded index into the tuple
boost::tuples::element<0,my_tuple>::type first=14;
boost::tuples::element<1,my_tuple>::type second=27;
The last declaration retrieves the index of the last element of the tuple, and uses that as input to the element helper to (generically) declare the type
boost::tuples::element<
boost::tuples::length<my_tuple>::value-1,my_tuple>::type last;
Tuples and for_each
The method that we used to create the print_all function can be extended to create a more general mechanism like std::for_each For example, what if we didn't want to print the elements, but rather wanted to sum them or copy them,
Trang 6or what if we wanted to print only some of them? Sequential access to the tuple elements isn't straightforward, as we discovered when we developed the
preceding examples It makes sense to create a general solution that accepts a function or function object argument to invoke on the tuple elements This
enables not only the (rather limited) print_all function's behavior, but also that of any function that can accept the types of elements from a tuple The following example creates a function