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.. Of course, this
Trang 1Here 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
Trang 2Found 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();
}
Trang 3};
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
Trang 4of 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
Trang 5tool 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 values 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
Trang 6capabilities 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 applied 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