O''''Reilly Network For Information About''''s Book part 88 ppt

6 214 0
O''''Reilly Network For Information About''''s Book part 88 ppt

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

Thông tin tài liệu

bind(&derived::do_more_stuff,var(pd))]. else_[bind(&base::do_stuff,*_1)])(p1); (if_(var(pd)=ll_dynamic_cast<derived*>(_1)) [bind(&derived::do_more_stuff,var(pd))]. else_[bind(&base::do_stuff,*_1)])(p2); } In main, the first thing we do is create p1 and p2; p1 points to a base, whereas p2 points to an instance of derived. In the first lambda expression, the assigned pd becomes the condition; it is implicitly converted to bool, and if it yields TRue, then-part is evaluated. Here, we bind to the member function do_more_stuff. If the ll_dynamic_cast fails, the delayed variable representing pd will be 0, and the else-part is executed. So, in our example, the first invocation of the lambda expression should call do_stuff on base, and the second should call do_more_stuff in derived, which is confirmed when running this program. void base::do_stuff() const void derived::do_more_stuff() const Note that in the example, the argument _1 is dereferenced, but this is not really necessary; this is done implicitly if needed. If an argument to a bind expression must always be a pointer type, you can enforce that by dereferencing it yourself. Otherwise, leave that chore to Boost.Lambda. ll_static_cast is really useful to avoid warnings. Don't use it to suppress important information, but to reduce noise. In a previous example, we created a bind expression that evaluated the length of a std::string (using std::string::size) and compared the length to another integral value. The return type of std::string::size is an unsigned type, and passing a signed integer type to the comparison (most likely) produces a warning from the compiler that signed and unsigned comparisons are risky business. However, because this happens in a lambda expression, the compiler dutifully traces the root of the problem by telling you which part of a nested template invocation is responsible for this horrible crime. The result is a very long warning message, which probably hides any other issues because of the low signal-to-noise ratio. In generic code, this can sometimes be an issue, because the types that are used are not within our control. Thus, after evaluating the potential problem, you often find it beneficial to suppress unwanted warnings using ll_static_cast. The following example includes code that exhibits this behavior. #include <iostream> #include <string> #include <algorithm> #include "boost/lambda/lambda.hpp" #include "boost/lambda/casts.hpp" #include "boost/lambda/if.hpp" #include "boost/lambda/bind.hpp" template <typename String,typename Integral> void is_it_long(const String& s,const Integral& i) { using namespace boost::lambda; (if_then_else(bind(&String::size,_1)<_2, var(std::cout) << "Quite short \n", std::cout << constant("Quite long \n")))(s,i); } int main() { std::string s="Is this string long?"; is_it_long(s,4u); is_it_long(s,4); } The parameterized function is_it_long (and please try to ignore that this is a slightly more contrived example than usual) invokes a lambda expression using a reference to const variable of type Integral. Now, whether this type is signed or not is beyond our control, so chances are good that a user will inadvertently trigger a very verbose warning, which is exactly what the example illustrates, because one call to is_it_long uses a signed integer. is_it_long(s,4); The only way to make sure that the user doesn't accidentally cause this to happen (besides requiring only unsigned types) is to make the argument an unsigned integer type, regardless of what it originally is. This is a job for ll_static_cast, so we change the function is_it_long like so: template <typename String,typename Integral> void is_it_long(const String& s,const Integral& i) { using namespace boost::lambda; (if_then_else(bind(&String::size,_1)< ll_static_cast<typename String::size_type>(_2), var(std::cout) << "Quite short \n", std::cout << constant("Quite long \n")))(s,i); } This situation does not arise often (at least I haven't seen it many times), but it does happen, and this solution works. Using ll_const_cast and ll_reinterpret_cast is similar to what we've seen here, so this example ends the cast functions. Use them wisely, and don't use ll_reinterpret_cast at all, without extremely compelling reasons (I can't think of any). It's mainly there for symmetry; if you need it, chances are good that you've done something that you shouldn't have. Constructing and Destructing When the need to create or destroy objects arises in lambda expressions, some special handling and syntax is required. To begin with, it's not possible to take the address of constructors or destructors, and it's thus not possible to use a standard bind expression for them. Moreover, operators new and delete have fixed return types, so they cannot return lambda expressions for arbitrary types. If you need to create or destroy objects in lambda expressions, make sure to include the header "boost/lambda/construct.hpp", which contains the templates constructor, destructor, new_ptr, new_array, delete_ptr, and delete_array. We'll take a look at how to use them, and focus on constructor and new_ptr, which are the most commonly used of these constructs. For our first example, consider a container that holds smart pointers as its elements, and we'll want to reset the contents of smart pointers in our lambda expression. This typically involves a call to operator new; the exception to that rule would be if some custom allocation scheme were used, or a factory method of some kind. We will need to use new_ptr to do that, and if you want or need to, it's often possible to also use constructor in an assignment expression. Let's do both. We'll set the table by defining two classes, base and derived, and a std::map of boost::shared_ptr<base>s indexed by std::strings. Take a deep breath before reading the lambda expressions in this example; they are two of the most complex lambda expressions you'll see in this chapter. Although complex, understanding what they do should be reasonably straightforward. Just take your time. #include <iostream> #include <map> #include <string> #include <algorithm> #include "boost/lambda/lambda.hpp" #include "boost/lambda/construct.hpp" #include "boost/lambda/bind.hpp" #include "boost/lambda/if.hpp" #include "boost/lambda/casts.hpp" #include "boost/shared_ptr.hpp" class base { public: virtual ~base() {} }; class derived : public base { }; int main() { using namespace boost::lambda; typedef boost::shared_ptr<base> ptr_type; typedef std::map<std::string,ptr_type> map_type; map_type m; m["An object"]=ptr_type(new base); m["Another object"]=ptr_type(); m["Yet another object"]=ptr_type(new base); std::for_each(m.begin(),m.end(), if_then_else(!bind(&ptr_type::get, bind(&map_type::value_type::second,_1)), (bind(&map_type::value_type::second,_1)= bind(constructor<ptr_type>(),bind(new_ptr<derived>())), var(std::cout) << "Created a new derived for \"" << bind(&map_type::value_type::first,_1) << "\".\n"), var(std::cout) << "\"" << bind(&map_type::value_type::first,_1) << "\" already has a valid pointer.\n")); m["Beware, this is slightly tricky"]=ptr_type(); std::cout << "\nHere we go again \n"; std::for_each(m.begin(),m.end(), if_then_else(!bind(&map_type::value_type::second,_1), ((bind(static_cast<void (ptr_type::*)(base*)> (&ptr_type::reset<base>), bind(&map_type::value_type::second,_1), bind(new_ptr<base>()))), var(std::cout) << "Created a new derived for \"" << bind(&map_type::value_type::first,_1) << "\".\n"), var(std::cout) << "\"" << bind(&map_type::value_type::first,_1) << "\" already has a valid pointer.\n")); } You got all of that, right? Just in case there was any confusion, I'll explain what's happening in this example. First, note that the two lambda expressions do essentially the same thing. They set a valid pointer for any element in the std::map that is currently null. Here's the output when running the program: "An object" already has a valid pointer. Created a new derived for "Another object". "Yet another object" already has a valid pointer. "An object" already has a valid pointer. "Another object" already has a valid pointer. "Yet another object" already has a valid pointer. Here we go again "An object" already has a valid pointer. "Another object" already has a valid pointer. Created a new derived for "Beware, this is slightly tricky". "Yet another object" already has a valid pointer. The output shows that we managed to put valid objects into each element of the map, but how? The expressions do a similar task, but each takes a different tack. Starting with the first one, let's dissect the lambda expression to see how it works. The first part is the condition, of course, which is quite trivial: [8] [8] It can be made even more trivial, as we shall soon see. !bind(&ptr_type::get,bind(&map_type::value_type::second,_1)) Seeing it like this makes it a bit easier, right? Reading the expression starting with the innermost bind tells us that we're binding to the member map_type::value_type::second (which is a ptr_type), and to that we bind the member function ptr_type::get (which returns the shared_ptr's pointee), and to the whole expression, we apply the operator!. Because a pointer is implicitly convertible to bool, that's a valid Boolean expression. That takes care of the . can enforce that by dereferencing it yourself. Otherwise, leave that chore to Boost.Lambda. ll_static_cast is really useful to avoid warnings. Don't use it to suppress important information, . yields TRue, then -part is evaluated. Here, we bind to the member function do_more_stuff. If the ll_dynamic_cast fails, the delayed variable representing pd will be 0, and the else -part is executed compiler dutifully traces the root of the problem by telling you which part of a nested template invocation is responsible for this horrible crime. The result is a very long warning message, which

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