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

6 227 0
O''''Reilly Network For Information About''''s Book part 75 pot

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

Thông tin tài liệu

template called for_each_element to do just that. For the sake of argument, the example shows two function objects to show the workings of for_each_element. #include <iostream> #include <string> #include <functional> #include "boost/tuple/tuple.hpp" template <typename Function> void for_each_element( const boost::tuples::null_type&, Function) {} template <typename Tuple, typename Function> void for_each_element(Tuple& t, Function func) { func(t.get_head()); for_each_element(t.get_tail(),func); } struct print { template <typename T> void operator()(const T& t) { std::cout << t << '\n'; } }; template <typename T> struct print_type { void operator()(const T& t) { std::cout << t << '\n'; } template <typename U> void operator()(const U& u) {} }; int main() { typedef boost::tuple<short,int,long> my_tuple; boost::tuple<int,short,double> nums(1,2,3.01); for_each_element(nums, print()); for_each_element(nums, print_type<double>()); } The function for_each_element reuses the strategy from earlier examples, by overloading the function with a version that accepts an argument of type null_type that signals the end of the tuple's elements, to do nothing. Let's look at the function where the work is done. template <typename Tuple, typename Function> void for_each_element(Tuple& t, Function func) { func(t.get_head()); for_each_element(t.get_tail(),func); } The second template and function parameter specifies the function (or function object) to call with the tuple elements as argument. for_each_element first invokes the function (object) with the element returned from get_head . One way to think of it is that get_head returns the current element of the tuple. Then, it recursively calls itself with the tail or remaining elements of the tuple. The next call extracts the head element and calls the function (object) with it and recurses again, and so on. Eventually, get_tail finds no more elements and returns an instance of null_type, which ends the recursion by matching the non-recursive for_each_element overload. That's all there is to for_each_element! Next, the example illustrates two function objects that contain nice techniques for reuse in other contexts. One is the print function object. struct print { template <typename T> void operator()(const T& t) { std::cout << t << '\n'; } }; There is nothing fancy about this print function object, but as it turns out, many programmers are unaware of the fact that the function call operator can be templated! Typically, function objects are parameterized on one or more types that they should work with, but that doesn't work for tuples because the elements are typically of different types. Thus, the parameterization is not on the function object itself, but on the function call operator, with the added benefit that using it is much simpler, as shown here. for_each_element(nums, print()); There's no need to specify the type, which would have been required with a parameterized function object. Pushing the template parameters onto the member functions of a class is sometimes useful and often user-friendly. The second function object prints all elements of a certain type. This kind of filtering can be used to extract elements of compatible types, too. template <typename T> struct print_type { void operator()(const T& t) { std::cout << t << '\n'; } template <typename U> void operator()(const U& u) {} }; This function object displays another useful technique, which I refer to as the discarding overload. It's used to ignore the elements passed to it except those of type T untouched. The trick involves an overload with a better match for all but a certain type. That bell you hear ringing is probably from the close connection this technique has with the sizeof trick and the ellipsis ( ) construct, which is used to make decisions at compile time, but that doesn't work here, where the function is actually called but doesn't do anything. The function object is used like this: for_each_element(print_type<double>(),nums); Easy to use, easy to write, and it adds value. That's probably not as much a property of the function object as it is of the Tuple library that enables the use of these and other idioms. Tuple Summary The Tuple library brings the concept of tuples to C++. It is intuitive and concise, and although its primary use seems to be for multiple return value from functions, it is also very useful for creating all sorts of logical groupings such as storing sets of elements (as elements) in Standard Library containers. The alternative for a chieving the same level of coherency is to create unique structs for every different return type (groupings), which is not only tedious work, it also removes the possibility of generic solutions for recurring tasks. These problems are alleviated with the use of the Boost.Tuple. In this chapter, we've seen how to use the Tuple library and how to extend it in the form of function objects and algorithms that can work with any tuple. Accessing elements by index, and the get_head/get_tail member functions, provides consistency in working with tuples that enables many solutions that are impossible with other forms of user-defined types (UDTs). The creator of Boost.Tuple, Jaakko Järvi, deserves credit for this great library. This creation goes a long way to prove that nearly anything lacking in C++ can be added through libraries by talented designers. How Does the Bind Library Improve Your Programs?  Adapts functions and function objects for use with Standard Library algorithms  Consistent syntax for creating binders  Powerful functional composition When using the algorithms from the Standard Library, you often need to supply them with a function or a function object. This is an excellent way of customizing the behavior of algorithms, but you often end up writing new function objects because you don't have the tools necessary for functional composition and adaptation of argument order or arity. Although the Standard Library does offer some productive tools, such as bind1st and bind2nd, this is rarely enough. Even when the functionality suffices, that often implies suffering from awkward syntax that obfuscates the code for programmers who are not familiar with those tools. What you need, then, is a solution that both adds functionality and normalizes the syntax for creating function objects on-the-fly, and this is what Boost.Bind does. In effect, a generalized binder is a sort of lambda expression, because through functional composition we can more or less construct local, unnamed functions at the call site. There are many cases where this is desirable, because it serves three purposesreducing the amount of code, making the code easier to understand, and localizing behavior, which in turn implies more effective maintenance. Note that there is another Boost library, Boost.Lambda, which takes these properties even further. Boost.Lambda is covered in the next chapter. Why shouldn't you just skip ahead to that library? Because most of the time, Boost.Bind does everything you need when it comes to binding, and the learning curve isn't as steep. One of the keys to the success of Bind is the uniform syntax for creating function objects and the few requirements on types that are to be used with the library. The design takes focus away from how to write the code that works with your types, and sets it to where we are all most interestedhow the code works and what it actually does. When using adaptors from the Standard Library, such as ptr_fun and mem_fun_ref, code quickly becomes unnecessarily verbose because we have to provide these adaptors in order for the arguments to adhere to the requirements of the algorithms. This is not the case with Boost.Bind, which uses a much more sophisticated deduction system, and a straightforward syntax when the automatic deduction cannot be applied. The net effect of using Bind is that you'll write less code that is easier to understand. How Does Bind Fit with the Standard Library? Conceptually, Bind is a generalization of the existing Standard Library functions bind1st and bind2nd, with additional functionality that allows for more sophisticated functional composition. It also alleviates the need to use adaptors for pointers to functions and pointers to class members, which saves coding and potential errors. Boost.Bind also covers some of the popular extensions to the C++ Standard Library, such as the SGI extensions compose1 and compose2, and also the select1st and select2nd functions. So, Bind does fit with the Standard Library, and it does so very well indeed. The need for such functionality is acknowledged, and at last in part addressed by the Standard Library, and also in popular extensions to the STL. Boost.Bind has been accepted for the upcoming Library Technical Report. How Does Bind Fit with the Standard Library? Conceptually, Bind is a generalization of the existing Standard Library functions bind1st and bind2nd, with additional functionality that allows for more sophisticated functional composition. It also alleviates the need to use adaptors for pointers to functions and pointers to class members, which saves coding and potential errors. Boost.Bind also covers some of the popular extensions to the C++ Standard Library, such as the SGI extensions compose1 and compose2, and also the select1st and select2nd functions. So, Bind does fit with the Standard Library, and it does so very well indeed. The need for such functionality is acknowledged, and at last in part addressed by the Standard Library, and also in popular extensions to the STL. Boost.Bind has been accepted for the upcoming Library Technical Report. . template called for_ each_element to do just that. For the sake of argument, the example shows two function objects to show the workings of for_ each_element. #include <iostream>. boost::tuple<int,short,double> nums(1,2,3.01); for_ each_element(nums, print()); for_ each_element(nums, print_type<double>()); } The function for_ each_element reuses the strategy from earlier. the non-recursive for_ each_element overload. That's all there is to for_ each_element! Next, the example illustrates two function objects that contain nice techniques for reuse in other

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

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan