Tải bản đầy đủ (.pdf) (6 trang)

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

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (22.63 KB, 6 trang )

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

×