O''''Reilly Network For Information About''''s Book part 66 pps

6 228 0
O''''Reilly Network For Information About''''s Book part 66 pps

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

Thông tin tài liệu

template <typename T> any_out(const T& value) : streamer_(new streamer_imp<T>),o_(value) {} Copy construction is straightforward; all we need is to make sure that the streamer in the source any_out a is not zero. [View full width] any_out(const any_out& a) : streamer_(a.streamer_?a.streamer_->clone():0),o_(a.o_) {} [1] template<typename T> any_out& operator=(const T& r) { any_out(r).swap(*this); return *this; } any_out& operator=(const any_out& r) { any_out(r).swap(*this); return *this; } ~any_out() { delete streamer_; } [1] Rob Stewart asked me whether I wrote this line to go for first prize in an obfuscation contest or if I just wanted to be able to write the ():0) emoticon. I'm not really sure, but decided to keep the line for your reading pleasure…. The swap function is supplied to facilitate exception-safe assignment. any_out& swap(any_out& r) { std::swap(streamer_, r.streamer_); std::swap(o_,r.o_); return *this; } And now, let's add what we came here for: the output operator. It should accept a reference to an ostream and an any_out. The any stored in the any_out should be passed on to the virtual function print of the streamer. friend std::ostream& operator<<(std::ostream& o,any_out& a) { if (a.streamer_) { a.streamer_->print(o,a.o_); } return o; } }; This class not only offers a way to perform stream output of arbitrary (unknown) types contained in a general class, it is also a display of how any is designed. This design, and the techniques used to safely wrap the type behind a polymorphic facade are general, and applicable in more cases than this. For instance, it would be possible to create a generic function adaptor. Let's take our any_out class for a test drive. int main() { std::vector<any_out> vec; any_out a(std::string("I do have operator<<")); vec.push_back(a); vec.push_back(112); vec.push_back(65.535); // Print everything in vector vec std::cout << vec[0] << "\n"; std::cout << vec[1] << "\n"; std::cout << vec[2] << "\n"; a=std::string("This is great!"); std::cout << a; } If the class X does not support operator<<, the code does not compile. Unfortunately, it doesn't matter whether we are actually going to use operator<< or not, it just doesn't work. any_out always requires that the output operator be available. any_out nope(X()); std::cout << nope; } Convenient, don't you think? If a certain operation is available for all types that you plan to use in a certain context, adding those can be done in the same way we did to supply operator<< for our any_out class. It is not much harder to generalize the solution and parameterize on the operations, which makes this solution for extending the interface of any reusable. Predicates Before we end this section on the usage of any, let's examine how to build functionality around any to simplify usage and to add expressive power. When any is used to enable storage of different types in container classes, it turns out that it's easy to store those values but quite hard to operate on them. First, we will create two predicates, is_int and is_string, which can be used to determine if an any contains an int or a string, respectively. These can be useful when we want to search for a particular type in a container of heterogeneous objects, or want to test the type of an any to determine further actions. The implementation uses the any member function type for the test. bool is_int(const boost::any& a) { return typeid(int)==a.type(); } bool is_string(const boost::any& a) { return typeid(std::string)==a.type(); } The preceding solution works, but it is tedious to write predicates for every type we are interested in testing for. The implementation is repeated, so this would be the perfect fit for a template solution like the following. template <typename T> bool contains (const boost::any& a) { return typeid(T)==a.type(); } The function contains saves us from having to manually create new predicates. This is a canonical example of how templates are used to minimize redundant coding. Counting Non-Empty Values For certain applications, it is useful to iterate over the elements of a container and test whether the anys contain a value or not. An empty any might imply that it should be removed, or perhaps we need to extract all non-empty elements of any for some further processing. To make this useful in an algorithm, we create a function object with a function call operator taking an any argument. The operator just tests whether the any is empty and, if it is not, increments the counter. class any_counter { int count_; public: any_counter() : count_(0) {} int operator()(const boost::any& a) { return a.empty() ? count_ : ++count_; } int count() const { return count_; } }; For a container C storing values of any, counting the non-empty values is accomplished like this. int i=std::for_each(C.begin(),C.end(),any_counter()).count(); Note that the for_each algorithm returns the function object, so we can easily access the count. Because for_each accepts its arguments by value, the following code does not accomplish the same thing. any_counter counter; std::for_each(C.begin(),C.end(),counter); int i=counter.count(); The second version always yields 0, because the function object counter simply is copied when calling the for_each. The first version works, because the returned function object (the copy of counter) is used for retrieving the count. Extracting Elements of Certain Types from a Container Here's an extra treat: An extractor for retrieving certain types from a container. This can be a useful utility when parts of a heterogeneous container are to be transferred to a homogeneous container. Manually, this is a tedious and error-prone task, but one simple function object takes care of everything for us. We will parameterize the function object on the type of output iterator for the retrieved elements, and the type to extract from the any arguments that are passed to it. template <typename OutIt,typename Type> class extractor { OutIt it_; public: extractor(OutIt it) : it_(it) {} void operator()(boost::any& a) { Type* t(boost::any_cast<Type>(&a)); if (t) { *it_++ = *t; } } }; As a convenience for creating an extractor, here's a function that deduces the type of the output iterator and returns an appropriate extractor. template <typename Type, typename OutIt> extractor<OutIt,Type> make_extractor(OutIt it) { return extractor<OutIt,Type>(it); } Using the Predicates and the Extractor It's high time to test our new any companions with a sample program. int main() { std::cout << "Example of using predicates and the " "function object any_counter\n"; std::vector<boost::any> vec; vec.push_back(boost::any()); for(int i=0;i<10;++i) { vec.push_back(i); } vec.push_back(boost::any()); We have added 12 any objects to the vec, and now we're interested in finding out how many of the elements contain a value. To count elements with values, we use the function object any_counter that we've created. // Count the instances of any that contain a value int i=std::for_each( vec.begin(), vec.end(), any_counter()).count(); std::cout << "There are " << i << " non-empty any's in vec\n\n"; . search for a particular type in a container of heterogeneous objects, or want to test the type of an any to determine further actions. The implementation uses the any member function type for. but it is tedious to write predicates for every type we are interested in testing for. The implementation is repeated, so this would be the perfect fit for a template solution like the following } }; For a container C storing values of any, counting the non-empty values is accomplished like this. int i=std: :for_ each(C.begin(),C.end(),any_counter()).count(); Note that the for_ each

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