Tải bản đầy đủ (.pdf) (6 trang)

O''''Reilly Network For Information About''''s Book part 74 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (22.38 KB, 6 trang )

int 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.
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 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
This 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
type 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;
boost::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,
or 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

×