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

6 200 0
O''''Reilly Network For Information About''''s Book part 65 pps

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

Thông tin tài liệu

std::cout << "This any contained a boost::shared_ptr<A>\n"; ptr-> is_virtual (); ptr->not_virtual(); return; } catch(boost::bad_any_cast& e) {} // Try boost::shared_ptr<B> try { boost::shared_ptr<B> ptr= boost::any_cast<boost::shared_ptr<B> >(a); std::cout << "This any contained a boost::shared_ptr<B>\n"; ptr-> is_virtual (); ptr->not_virtual(); return; } catch(boost::bad_any_cast& e) {} // If anything else (like just a string), ignore it std::cout << "The any didn't contain anything that \ concerns this function!\n"; } In main, we create two anys at function scope. We then introduce a new scope, and create two new anys. Next, we store all of the anys in the vector and send every element in it to the function foo, which examines their contents and exercises them. It should be duly noted that we are actually violating the advice that was given earlier, to use the pointer form of any_cast when a failure does not designate an error. However, because we are dealing with smart pointers here, the syntactic advantage of using the exception-throwing form of any_cast is reason enough to ignore the advice this time. int main() { std::cout << "Example of any and shared_ptr\n"; boost::any a1(boost::shared_ptr<A>(new A)); boost::any a2(std::string("Just a string")); { boost::any b1(boost::shared_ptr<A>(new B)); boost::any b2(boost::shared_ptr<B>(new B)); std::vector<boost::any> vec; vec.push_back(a1); vec.push_back(a2); vec.push_back(b1); vec.push_back(b2); std::for_each(vec.begin(),vec.end(),foo); std::cout << "\n"; } std::cout << "any's b1 and b2 have been destroyed which means\n" "that the shared_ptrs' reference counts became zero\n"; } When this program is run, it produces the following output. Example of any and shared_ptr This any contained a boost::shared_ptr<A> A:: is_virtual () A::not_virtual() The any didn't contain anything that concerns this function! This any contained a boost::shared_ptr<A> B:: is_virtual () A::not_virtual() This any contained a boost::shared_ptr<B> B:: is_virtual () B::not_virtual() A::~A() A::~A() First, we see that the any passed to foo contains a shared_ptr<A>, which also happens to own an instance of A. The output is what one would expect. Next, the any contains the string that we added to our vector. This shows that it is quite possible, and often reasonable, to store types that are unknown to some of the functions that will be called with an any argument; the functions only need to handle the types they are required to operate on! Then things get really interestingthe third element contains a shared_ptr<A> that is pointing to an instance of B. This is an example of how polymorphism works just the same for any as for other types. Of course, if we were using raw pointers, we could have used static_cast to store the pointer as the type that we want to be the identification tag that unlocks the any. Note that the function A::not_virtual is called instead of B::not_virtual. The reason is that the static type of the pointer is A*, not B*. The final element contains a shared_ptr<B> that also points to an instance of B. Again, we are controlling the type stored in any, which sets the preferences for those who later try to unlock it. At the end of the inner scope, the vector is destroyed, which destroys the contained instances of any, which in turn destroys the shared_ptrs, effectively setting the reference counts to zero. Consequently, this means that our pointers are safely and effortlessly destroyed as well! This example shows something that's more important than how to use smart pointers together with any; it (again) shows that it doesn't really matter how simple or complex the type that we store in any is. If the cost of copying the stored values is prohibitive, or if shared usage and lifetime control is an issue, consider using smart pointers, just as when using the Standard Library containers to store the values. The exact same reasoning applies equally well to using any, and often the two principles coincide, as it is common to use any as a means to store heterogeneous types in containers. What About Input and Output Operators? A common question from users of any is "why aren't there input and output operators?" There are indeed good reasons for that. Let's start with the input operator. What would be the semantics for input? Would it default to a string type? Would the current type held by the any be used for extraction from the stream? If so, why would an any be used in the first place? These questions come without good answers, and that's the reason why there's no input operator for any. Answering the second question is not as easy, but almost. Supplying a forwarding output operator for any would mean that any is no longer capable of storing arbitrary types as that operator would impose the same requirement on the types stored by any. It wouldn't even matter if we never intended to use operator<<; an instantiation of an any containing a type that doesn't have an output operator is still illegal, and results in an error when compiling. Of course, were we to provide a template version of operator<<, we would be able to use any without requiring that the contained types support streaming, but as soon as that operator is instantiated, the requirement is on. There seems to be a cure for missing operators, right? What if we were to supply a valid output operator for any that matches anything, and introduce that operator<< in a scope that is only accessible from the implementation details of the any class? That way, we could elect to throw an exception or return an error code when an output to a stream was performed (the function would only match for arguments without support for operator<<), and we could do this at runtime, without affecting the legality of any other code. This idea struck me as so appealing that I gave it a try on a few of the compilers I have at hand. The results were not good. I won't go into detail, but in short, the solution requires techniques that many compilers can't currently handle. However, we don't necessarily need to change the any classwe could create a new class that takes advantage of any to store arbitrary types, and have that class support operator<<. Basically, we need to do whatever it is any does to keep track of the contained type to know how to write the output, and then add the output streaming. Adding Support for Outputany_out We will define a class that is capable of output through operator<<. This adds to the requirements of the types that are to be stored; to be a valid type for storage in the class any_out, the type must support operator<<. #include <iostream> #include <vector> #include <string> #include <ostream> #include "boost/any.hpp" class any_out { The any_out class stores the (arbitrary) value in a datum of type boost::any. Always select reuse over reinvention! boost::any o_; Next, we declare an abstract class streamer, which uses the same design as any. We cannot use a parameterized type directly, because we would then need to parameterize any_out as well, which in turns makes the type of any_out dependent on the type of its contained value, effectively rendering the class useless in the context of heterogeneous storage. The type of the contained value must not be a part of the signature for the any_out class. struct streamer { virtual void print(std::ostream& o,boost::any& a)=0; virtual streamer* clone()=0; virtual ~streamer() {} }; Here's the trick: We add a parameterized class, streamer_imp, parameterized on the contained type and inheriting from streamer. Thus, we are able to store a pointer to streamer in any_out, and rely on polymorphism to do the rest of the work (next, we'll add a virtual member function for that purpose). template <typename T> struct streamer_imp : public streamer { Now, let's implement a virtual function print to output the value contained in the any by performing an any_cast on the type that streamer_imp is parameterized with. Because we're going to instantiate a streamer_imp on the same type as the value put in the any, the cast doesn't fail. virtual void print(std::ostream& o,boost::any& a) { o << *boost::any_cast<T>(&a); } A cloning function is needed when an any_out is being copiedwe are going to store a pointer to streamer, so the virtual function clone takes care of copying the correct type of streamer. virtual streamer* clone() { return new streamer_imp<T>(); } }; class any_out { streamer* streamer_; boost::any o_;public: The default constructor creates an empty any_out, and sets the streamer pointer to zero. any_out() : streamer_(0) {} The most interesting function for any_out is the parameterized constructor. The type T, deduced from the type of the value to store, is used when creating the streamer. The value is stored in the any o_. . reasons for that. Let's start with the input operator. What would be the semantics for input? Would it default to a string type? Would the current type held by the any be used for extraction. reason why there's no input operator for any. Answering the second question is not as easy, but almost. Supplying a forwarding output operator for any would mean that any is no longer capable. exception or return an error code when an output to a stream was performed (the function would only match for arguments without support for operator<<), and we could do this at runtime, without

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