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

HandBooks Professional Java-C-Scrip-SQL part 89 ppsx

5 89 0
Tài liệu đã được kiểm tra trùng lặp

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 21,87 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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

Trang 1

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),

Trang 2

((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

Trang 3

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>(

Trang 4

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

Trang 5

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."

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