O''''Reilly Network For Information About''''s Book part 67 potx

6 245 0
O''''Reilly Network For Information About''''s Book part 67 potx

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

Thông tin tài liệu

Here is how the extractor function object that operates on a container of anys works, populating a new container with a certain type collected from the source container. // Get all ints in vec std::list<int> lst; std::for_each(vec.begin(),vec.end(), make_extractor<int>(std::back_inserter(lst))); std::cout << "Found " << lst.size() << " ints in vec\n\n"; Let's clear the contents of the container vec and add some new values. vec.clear(); vec.push_back(std::string("This is a string")); vec.push_back(42); vec.push_back(3.14); Now, let's try the predicates that we created. First, we use the two predicates that indicate whether an any contains a string or an int, respectively. if (is_string(vec[0])) { std::cout << "Found me a string!\n"; } if (is_int(vec[1])) { std::cout << "Found me an int!\n"; } As we concluded earlier, defining predicates for every type we are ever interested in is tedious and utterly unnecessary, when we can use the language to our advantage in a straightforward manner. if (contains<double>(vec[2])) { std::cout << "The generic tool is sweeter, found me a double!\n"; } } Running this example gives you this output. Example of using predicates and the function object any_counter There are 10 non-empty any's in vec Found 10 ints in vec Found me a string! Found me an int! The generic tool is sweeter, found me a double! Small and simple tools like these have proven to be very useful. Of course, this is not only true for any; it's a property of the design of the Standard Library containers and algorithms. The examples show how to take advantage of function composition together with any . Providing filtering, counting, operations on certain types, and so forth are powerful ways of hiding implementation details, and simplifying the usage of any. Complying with the Requirements of Standard Library Adapters If you found the predicate contains useful, you may have noticed that it is not quite all it can be. There is no way to use it together with the Standard Library adapters. The following example is slightly outside the scope of this chapter, but because any fits so well with the container classes, it would be a shame to leave a somewhat flawed predicate of contains as is. The problem is that the Standard Library adapters (bind1st, bind2nd, not1, and not2) impose requirements on the predicates they adapt. The type of the argument and the result type must be exposed through provided typedefs, and that means that we need a function object rather than a function. First comes the definition of our new function object, contains_t . It could have inherited from the helper class std::unary_function (part of the C++ Standard Library, intended to facilitate the creation of the correct typedefs) and have the argument and result types defined automatically, but to make things clear, the required typedefs are provided explicitly. The argument type has changed from const boost::any& to boost::any, to avoid a potential reference-to- reference, which is illegal. The implementation is just as before, only here it is placed in the function call operator. template <typename T> struct contains_t { typedef boost::any argument_type; typedef bool result_type; bool operator()(boost::any a) const { return typeid(T)==a.type(); } }; To save the name contains for subsequent use in the helper function that's soon to come, the name of the function object is contains_t. Here is a helper function that creates and returns an instance of contains_t with the appropriate type set automatically. The reason is that we want to overload contains so that we are still able to provide the original predicate that we created. template <typename T> contains_t<T> contains() { return contains_t<T>(); } Finally, the good old predicate is changed to take advantage of the contains_t implementation. Now, if we need to change the implementation of contains_t for some reason, contains will reflect those changes without any further effort. template <typename T> bool contains(const boost::any& a) { return contains_t<T>()(a); } Here's a sample program that demonstrates what we have gained, using both the new function object and the predicate from the previous example. int main() { std::cout << "Example of using the improved is_type\n"; std::vector<boost::any> vec; vec.push_back(std::string("This is a string")); vec.push_back(42); vec.push_back(3.14); Using the predicate is no different than before. Testing an any for a certain type is still easy. if (contains<double>(vec[2])) { std::cout << "The generic tool has become sweeter! \n"; } vec.push_back(2.52f); vec.push_back(std::string("Another string")); Another example of the use of contains is to search a container for occurrences of a certain type. This example finds the first float. std::vector<boost::any>::iterator it=std::find_if(vec.begin(),vec.end(),contains<float>()); As yet another reminder, the two ways of retrieving the contained value of an any are demonstrated. Pass the any to any_cast by const reference for the exception-throwing version. Pass the address of the any to return a pointer to the stored value. if (it!=vec.end()) { std::cout << "\nPrint the float twice!\n"; std::cout << boost::any_cast<float>(*it) << "\n"; std::cout << *boost::any_cast<float>(&*it) << "\n"; } std::cout << "There are " << vec.size() << " elements in vec\n"; I still haven't given a good example of why contains should be a full-fledged function object. In many cases, the reasons why may not be known beforehand, because we cannot anticipate every situation that our implementations will face. That's a strong reason to comply with the requirements of the Standard Library facilities, preferably in more than just the use cases that we are currently aware of. Nevertheless, I do have an example for you: The task is to remove all elements from a container vec that do not contain strings. Of course, writing another predicate that does the exact opposite of contains is one alternative, but that's an alternative that quickly can lead to maintenance nightmares, because of proliferation of function objects with similar work descriptions. The Standard Library provides us with an adapter called not1, which negates the result of an invocation of a function object, and this makes it trivial to clean out all non- string elements from our vector vec. vec.erase(std::remove_if(vec.begin(),vec.end(), std::not1(contains<std::string>())),vec.end()); std::cout << "Now, there are only " << vec.size() << " elements left in vec!\n"; } The examples in this section have demonstrated how to make effective use of any . Because the type of the stored value is not part of any's type, any is an essential tool when providing storage without imposing requirements on the stored types, including inheriting from a certain base class. We have seen that there is a price for this type hiding. any disallows access to the stored value without knowledge of the value's type, restricting opportunities to operate on the stored v alues. To a large extent, this can be amended by creating helper classespredicates and function objectsthat provide the necessary logic to access the values. Any Summary Discriminated types can contain values of different types and are quite different from indiscriminate (read void*) types. We always depend heavily on type safety in C++, and there are few situations in which we are willing to do without it. This is for good reasons: Type safety keeps us from making mistakes and improves the performance of our code. So, we avoid indiscriminate types. Still, it is not uncommon to find oneself in need of heterogeneous storage, or to insulate clients from the details of types, or to gain the utmost flexibility at lower levels of a hierarchy. any provides this functionality while maintaining full type safety, and that makes it an excellent addition to our toolbox! Use the Any library when  You need to store values of heterogeneous types in containers  Storage for unknown types is required  Types are being passed through layers that need not know anything about the types The design of Any also serves as a valuable lesson on how to encapsulate a type without effect on the type of the enclosing class. This design can be used to create generic function objects, generic iterators, and much more. It is an example of the power of encapsulation and polymorphism in conjunction with templates. In the Standard Library, there are excellent tools for storing collections of elements. When the need for storage of heterogeneous types arises, we want to avoid having to use new collection types. any offers a solution that works in many cases with existing containers. In a way, the template class any extends the capabilities of the Standard Library containers by packaging disparate types in a homogeneous wrapper that allows them to be made elements of those aforementioned containers. Adding Boost.Any to an existing code base is straightforward. It doesn't require changes to the design, and immediately increases flexibility where it's a pplied. The interface is small, making it a tool that is easily understood. The Any library was created by Kevlin Henney, and like all Boost libraries, has been reviewed, influenced, and refined by the Boost community. . earlier, defining predicates for every type we are ever interested in is tedious and utterly unnecessary, when we can use the language to our advantage in a straightforward manner. if (contains<double>(vec[2])). Now, if we need to change the implementation of contains_t for some reason, contains will reflect those changes without any further effort. template <typename T> bool contains(const boost::any&. vec.push_back(42); vec.push_back(3.14); Using the predicate is no different than before. Testing an any for a certain type is still easy. if (contains<double>(vec[2])) { std::cout

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