1. Trang chủ
  2. » Công Nghệ Thông Tin

O''''Reilly Network For Information About''''s Book part 89 pdf

5 228 0

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

THÔNG TIN TÀI LIỆU

condition, so we move on to the then-part. bind(&map_type::value_type::second,_1)= bind(constructor<ptr_type>(), bind(new_ptr<derived>())), There are three bind expressions here, the first one (we start reading from the left here, because the expression involves an assignment) extracts the member map_type::value_type::second, which is the smart pointer. This is the value that we assign a new derived to. The second and third expressions are nested, so we read them from the inside out. The innermost bind takes care of the default construction of an instance of derived on the heap, and to the result we bind a constructor call to ptr_type (the smart pointer type), which is then assigned (using the usual notation for assignment) to the very first bind expression. Then, we add another expression to this then-part, which simply prints out a short message and the element's key. var(std::cout) << "Created a new derived for \"" << bind(&map_type::value_type::first,_1) << "\".\n") Finally, we add the else-part of the statement, which prints out the key of the element and some text. var(std::cout) << "\"" << bind(&map_type::value_type::first,_1) << "\" already has a valid pointer.\n")); When decomposing the expressions, it's clear that they're not really that complex, although looking at the whole thing can be quite intimidating. It's important to indent and separate the code so that reading becomes intuitive. We can write a similar expression for accomplishing our task, in a version that's quite different from this one but is much harder to read, although it is slightly more efficient. The thing to note here is that there are often several ways of attacking the problem of writing lambda expressions, just as is the case with other programming problems. It makes sense to apply some extra thought before writing, because the choices substantially affect the readability of the end result. For comparison, here's the other version I mentioned: 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<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")); This is not as nice, because the code is cluttered with casts and complicated nested binds, and we move away from the actual logic more than the previous version did. To understand it, let's again decompose the expressions to their constituent parts. First, we have the condition, which is actually simplified (nothing else is in this expression!); we utilize our knowledge of shared_ptr, which tells us that there is an implicit conversion to bool available. We can thus eliminate the bind to the member function get that we used in the previous expression. !bind(&map_type::value_type::second,_1) That condition works with the original expression, too. The next part is this: bind(static_cast<void (ptr_type::*)(base*)> (&ptr_type::reset<base>), bind(&map_type::value_type::second,_1), bind(new_ptr<derived>())) This is arguably too hard to parse, so we should have avoided it in the first place. Rather than using assignment, we go directly for the member function reset, which is not only parameterized but also overloaded. We thus need to perform a static_cast to tell the compiler which version of reset we are interested in. In this case, it is mainly the static_cast that complicates the reading of the expression, but again starting from the innermost expression, we can work through it. We bind a call to operator new, creating an instance of derived, and to the result we bind the smart pointer (through the member map_type::value_type::second), to which we bind the shared_ptr member function reset. This results in a call to reset for the smart pointer in the element, with the argument being a newly constructed instance of derived. Although we've done basically the same thing as in the previous example, this version is much harder to understand. Just remember that there can often be alternatives that lead to lambda expressions that are easier or harder to read and understand, so consider the alternatives and choose the easier forms when possible. It is imperative to treat the power that this library offers, and the effect it can have on your fellow programmers, with respect. Throwing and Catching Exceptions We have reached the final section of this chapter, which discusses exception handling in lambda expressions. If your reaction to this topic is to wonder exactly what justifies exception handling code in a lambda expression, that matches my first thoughts fairly well. However, it's not as far fetched as you might think. Surely you have written code that performs local exception handling when processing data in a loop? Well, handwritten loops can be avoided through usage of the Boost.Lambda library, so moving that exception handling into lambda expressions is quite natural. To use the exception handling facilities of Boost.Lambda, include "boost/lambda/exceptions.hpp". Let's reuse the classes base and derived that we saw earlier, and perform dynamic_casts almost like we did beforebut this time we will perform casts to references rather than pointers, which means that upon failure, dynamic_cast will throw an exception. This makes the example more straightforward than what we did before, because we don't need to use an if statement. #include <iostream> #include "boost/lambda/lambda.hpp" #include "boost/lambda/casts.hpp" #include "boost/lambda/bind.hpp" #include "boost/lambda/exceptions.hpp" int main() { using namespace boost::lambda; base* p1=new base; base* p2=new derived; (try_catch( bind(&derived::do_more_stuff,ll_dynamic_cast<derived&>(*_1)), catch_exception<std::bad_cast>(bind(&base::do_stuff,_1))))(p1); (try_catch( bind(&derived::do_more_stuff, ll_dynamic_cast<derived&>(*_1)), catch_exception<std::bad_cast>( bind(&base::do_stuff,_1))))(p2); } These expressions reveal that you wrap an expression in a call to TRy_catch. The general form of try_catch is try_catch(expression, catch_exception<T1>(expression), catch_exception<T2>(expression, catch_all(expression)) In the example code, the expressions use dynamic_casts to derived&. The first cast fails because p1 points to an instance of base; the second cast succeeds, because p2 points to an instance of derived. Note the dereferencing of the placeholders (*_1). This is required because we are passing pointers as arguments to the expressions, but the dynamic_casts we're interested in expect objects or references. If you need the try_catch to handle several types of exceptions, be sure to put the most specialized types first, just as with regular exception handling code. [9] [9] Otherwise, a more general type will match an exception and not find the handler for the more specific type. Consult your favorite C++ book for more details on this. If we want to access the actual exception that was caught, we can do so using a special placeholder, _e. Of course, one cannot do that in catch_all, just as there is no exception object in a catch ( ). Continuing the preceding example, we can print the reason for the failed dynamic_cast like so: try_catch( bind(&derived::do_more_stuff,ll_dynamic_cast<derived&>(*_1)), catch_exception<std::bad_cast> (std::cout << bind(&std::exception::what,_e))))(p1); When dealing with an exception type derived from std::exceptiona common caseyou can bind to the virtual member function what, as shown here. Sometimes, however, you don't want to catch an exception, but rather throw one. This is done via the function throw_exception. Because you need to create an exception object to throw, you'll typically use constructor to throw an exception from inside a lambda expression. The following example defines an exception class, some_exception, which inherits publicly from std::exception, and creates and throws one in a lambda expression if the argument to the expression is true. #include <iostream> #include <exception> #include "boost/lambda/lambda.hpp" #include "boost/lambda/exceptions.hpp" #include "boost/lambda/if.hpp" #include "boost/lambda/construct.hpp" #include "boost/lambda/bind.hpp" class some_exception : public std::exception { std::string what_; public: some_exception(const char* what) : what_(what) {} virtual const char* what() const throw() { return what_.c_str(); } virtual ~some_exception() throw() {} }; int main() { using namespace boost::lambda; try { std::cout << "Throw an exception here.\n"; (if_then(_1==true,throw_exception( bind(constructor<some_exception>(), constant("Somewhere, something went \ terribly wrong.")))))(make_const(true)); std::cout << "We'll never get here!\n"; } catch(some_exception& e) { std::cout << "Caught exception, \"" << e.what() << "\"\n"; } } Running this program yields the following output: Throw an exception here. Caught exception, "Somewhere, something went terribly wrong." . some extra thought before writing, because the choices substantially affect the readability of the end result. For comparison, here's the other version I mentioned: std: :for_ each(m.begin(),m.end(),. reuse the classes base and derived that we saw earlier, and perform dynamic_casts almost like we did beforebut this time we will perform casts to references rather than pointers, which means that. more general type will match an exception and not find the handler for the more specific type. Consult your favorite C++ book for more details on this. If we want to access the actual exception

Ngày đăng: 07/07/2014, 08:20

Xem thêm: O''''Reilly Network For Information About''''s Book part 89 pdf