The following examples all assume that the declarations in the namespace boost::lambda have been brought to the current scope through using declarations or a using directive... large eno
Trang 1if_then_else_return, which calls the conditional operator Let's take a closer look at the whole range of control structures, starting with if and switch Remember that to use if-constructs, "boost/lambda/if.hpp" must be included For switch, "boost/lambda/switch.hpp" must be included The following examples all assume that the declarations in the namespace
boost::lambda have been brought to the current scope through using
declarations or a using directive
(if_then(_1<5,
std::cout << constant("Less than 5")))(make_const(3));
The if_then function starts with a condition, followed by a then-part; in the preceding code, if the argument passed to the lambda function is less than 5
(_1<5), "Less than 5" is printed to std::cout You'll note that when we invoke this lambda expression with the numeric value 3, we cannot pass it directly, like so
(if_then(_1<5,std::cout << constant("Less than 5")))(3);
This would result in a compiler error, because 3 is an int, and an rvalue of type int (or any built-in type for that matter) cannot be const qualified Thus, one has to use the utility make_const here, which does nothing more than return a reference to const of its argument Another option is to wrap the whole lambda expression in a call to const_parameters, like so:
(const_parameters(
if_then(_1<5,std::cout << constant("Less than 5"))))(3);
const_parameters is useful to avoid having to wrap each of several
arguments with make_const Note that when using this function, all of the
parameters to the lambda expression are considered (references to) const
Now look at how if_then looks using the alternative syntax
(if_(_1<5)
[std::cout << constant("Less than 5")])(make_const(3));
This notation has a greater resemblance to the C++ keyword, but it does exactly the same thing as if_then The function if_ (note the trailing underscore) is
followed by the parenthesized condition, which in turn is followed by the
Trang 2then-statement Again, choosing between these syntax alternatives is simply a matter of taste
Now, let's take a look at the if-then-else constructs; they're very similar to
if_then
(if_then_else(
_1==0,
std::cout << constant("Nothing"),
std::cout << _1))(make_const(0));
(if_(_1==0)
[std::cout << constant("Nothing")]
else_[std::cout << _1])(make_const(0));
When adding the else-part using the alternative syntax, note that a period precedes the else_
The return type of these lambda expressions is void, but there is also a version that returns a value, by using the conditional operator There are some non-trivial rules for the types of such expressions (I won't go through them here, but see the online documentation for Boost.Lambda or the C++ Standard [§5.16] for the nitty-gritty details) Here's an example, where the return value is assigned to a variable, similar to how you would use the conditional operator for ordinary expressions
int i;
int value=12;
var(i)=(if_then_else_return
(_1>=10,constant(10),_1))(value);
There is no version of the alternative syntax for this construct That's it for if-then-else, which brings us to the switch-statement, which differs somewhat from the standard C++ switch
(switch_statement
_1,
case_statement<0>
(var(std::cout) << "Nothing"),
case_statement<1>
(std::cout << constant("A little")),
default_statement
Trang 3(std::cout << _1))
)(make_const(100));
The call to switch_statement starts with the condition variable, which in our case is _1, the first argument to the lambda expression This is followed by (up to nine) case constants, which have labels of integer type; these must be constant integral expressions We provide two such constants, for 0 and 1 (note that they could have any value acceptable for integral types) Finally, we add the optional default_statement, which is executed if the evaluation of _1 doesn't match any of the other constants Note that a break-statement is implicitly added to each case constant, so there's no need to explicitly exit from a switch (which is a Good Thing for those maintaining the code[6])
[6]
Spokesmen of fall-through case-statements; please excuse this blasphemy
Now let's examine the iteration statements, for, while, and do To use any of these, you must include the header "boost/lambda/loops.hpp" first
Boost.Lambda's equivalent of C++'s while is while_loop
int val1=1;
int val2=4;
(while_loop(_1<_2,
(++_1,std::cout << constant("Inc \n"))))(val1,val2);
A while_loop statement is executed until the condition becomes false; here the condition is _1<_2, which is followed by the body of the loop, the expression ++_1,std::cout << constant("Inc \n") Of course, the condition and the loop body must, themselves, be valid lambda expressions The alternative syntax is closer to the C++ syntax, just as was the case with if_
int val1=1;
int val2=4;
(while_(_1<_2)
[++_1,std::cout << constant("Inc \n")])(val1,val2);
The form is while_(condition)[substatement], and it does save a couple of keystrokes…but personally I find the function call syntax easier to read for
while, although I (irrationally) find if_ easier to parse than if_then( )
Go figure do_while_loop is naturally very similar to while_loop, but the
Trang 4substatement is always executed at least once (unlike while, the condition is evaluated after each execution)
(do_while_loop(_1!=12,std::cout <<
constant("I'll run once")))(make_const(12));
The corresponding alternative syntax is
(do_[std::cout <<
constant("I'll run once")].while_(_1!=12))(make_const(12));
Finally, there's the for loop equivalent, for_loop In the following example, a named, delayed variable is used to make the lambda expression more readable We've come across delayed variables before through the use of constant and var Delayed variables with names is a way of avoiding having to type
constant or var for constants and variables, respectively Instead, they're given
a name of your choice with which they can later be referred The general form for the loop is for_loop(init-statement, condition, expression, statement)that is, it's like a regular for statement but the statement is part of the function (arguments)
int val1=0;
var_type<int>::type counter(var(val1));
(for_loop(counter=0,counter<_1,++counter,var(std::cout)
<< "counter is " << counter << "\n"))(make_const(4));
With the alternative syntax, statement is separated from initialization, condition, and expression
(for_(counter=0,counter<_1,++counter)[var(std::cout)
<< "counter is " << counter << "\n"])(make_const(4));
The example initializes the delayed variable counter to 0, the condition is
counter<_1, and the expression is ++counter
This concludes the section on control structures For most problems that I've
encountered and solved with lambda expressions, I can actually do without them, but sometimes, they are real lifesavers Regarding the choice of syntactic version, the best way to figure out which to use is probably to experiment using both, and get a feel for which version suits your needs the best It should be noted that when using switch and the loop constructs, the lambda expressions quickly become
Trang 5large enough to make them hard to follow if you're not fairly accustomed to using the library Some care should thus be taken, and if an expression seems too hard to parse for your fellow programmers, consider a separate function object instead (Or have them practice using Boost.Lambda more!)
Casting in Lambda Expressions
There are four special "cast operators"[7] that allow casting of types in lambda expressions: ll_dynamic_cast, ll_static_cast,
ll_reinterpret_cast, and ll_const_cast The names are different from the corresponding C++ keywords because these cannot be overloaded To use these casts, include the header "boost/lambda/casts.hpp" These
functions work like their C++ cast operator equivalents; they take an explicit
template argument, which is the type to cast to, and an implicit template argument, which is the source type In our first example, we will use two classes,
imaginatively named base and derived We'll create two pointers to base, one
of them will point to an instance of base, and the other to an instance of
derived Using ll_dynamic_cast, we will try to extract a derived* from both of these pointers
[7]
Technically, they are template functions returning function objects
#include <iostream>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/casts.hpp"
#include "boost/lambda/if.hpp"
#include "boost/lambda/bind.hpp"
class base {
public:
virtual ~base() {}
void do_stuff() const {
std::cout << "void base::do_stuff() const\n";
}
};
class derived : public base {
public:
void do_more_stuff() const {
std::cout << "void derived::do_more_stuff() const\n";
}
};
Trang 6int main() {
using namespace boost::lambda;
base* p1=new base;
base* p2=new derived;
derived* pd=0;
(if_(var(pd)=ll_dynamic_cast<derived*>(_1))