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

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

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 (33.81 KB, 6 trang )

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.


×