1. Trang chủ
  2. » Công Nghệ Thông Tin

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

6 196 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 22,38 KB

Nội dung

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 . 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 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. 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

Ngày đăng: 07/07/2014, 08:20