Beyond the C++ Standard Library: An Introduction to Boost [Electronic resources] نسخه متنی

This is a Digital Library

With over 100,000 free electronic resource in Persian, Arabic and English

Beyond the C++ Standard Library: An Introduction to Boost [Electronic resources] - نسخه متنی

Bjorn Karlsson

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید

Usage


This library, like most other Boost libraries, is purely defined in header files, which means that you don't have to build anything to get started. However, understanding a little something about lambda expressions is definitely helpful. The following sections will walk you through this library, even including how to perform exception handling in lambda expressions! The library is quite extensive, and there's a lot of power waiting ahead. A lambda expression is often called an unnamed function. It is declared and defined when it's neededthat is, at the call site. This is very useful, because we often need to define an algorithm in another algorithm, something that isn't really supported by the language. Instead, we externalize behavior by bringing in functions and function objects from a wider scope, or use nested loop constructs with the algorithmic expressions encoded in the loops. As we shall see, this is where lambda expressions come to the rescue. This section consists of many examples, and there is often one part of the example that demonstrates how the solution would be coded using "traditional" tools. The intent is to show when and how lambda expressions help programmers write more logic with less code. There is a certain learning curve associated with lambda expressions, and the syntax may seem daunting at first glance. Like every new paradigm or tool, this one must be learnedbut trust me when I say that the profit definitely outweighs the cost.

A Little Teaser


The first program using Boost.Lambda should whet your appetite for lambda expressions. First of all, note that the lambda types are declared in the namespace boost::lambdatypically, you bring these declarations into scope with a using directive or using declarations. The core functionality of the library is available when including the file "boost/lambda/lambda.hpp", which is sufficient for our first program.


#include <iostream>
#include "boost/lambda/lambda.hpp"
#include "boost/function.hpp"
int main() {
using namespace boost::lambda;
(std::cout << _1 << " " << _3 << " " << _2 << "!\n")
("Hello","friend","my");
boost::function<void(int,int,int)> f=
std::cout << _1 << "*" << _2 << "+" << _3
<< "=" <<_1*_2+_3 << "\n";
f(1,2,3);
f(3,2,1);
}

The first expression looks peculiar at first glance, but it helps to mentally divide the expression as the parentheses do; the first part is a lambda expression that basically says, "print these arguments to std::cout, but don't do it right now, because I don't yet know the first, second, and third arguments." The second part of the expression actually invokes the function by saying, "Hey! Here are the three arguments that you need." Look at the first part of the expression again.


std::cout << _1 << " " << _3 << " " << _2 << "!\n"

You'll note that there are three placeholders, aptly named _1, _2, and _3, in the expression.[1] These placeholders denote the delayed arguments to the lambda expression. Note that unlike the syntax of many functional programming languages, there's no keyword or name for creating lambda expressions; it is the presence of the placeholders that signal that this is a lambda expression. So, this is a lambda expression that accepts three arguments of any type that support streaming through operator<<. The arguments are printed to cout in the order 1-3-2. Now, in the example, we enclose this expression in parentheses, and then invoke the resulting function object by passing three arguments to it: "Hello", "friend", and "my". This results in the following output:

[1] It may not have occurred to you before that identifiers like _1 are legal, but they are. Identifiers may not start with a number, but they may start with an underscore, and numbers can appear anywhere else in an identifier.


Hello my friend!

Typically, we use function objects to pass into algorithms, which we shall investigate further, but to try something a little more useful, let's store the lambda expression in another delayed function, namely boost::function. These useful creatures are described in the following chapter, "Library 11: Function 11," but for now, it suffices to know that you can pass a function or a function object to an instance of boost::function, and store it there for later invocation. In the example, we define such a function, f, like so:


boost::function<void(int,int,int)> f;

This declaration states that f can store functions and function objects that can be invoked with three arguments, all of the type int. Then, we assign such a function object using a lambda expression that captures the algorithm X=S*T+U, and then prints the expression and the result to cout.


boost::function<void(int,int,int)> f=
std::cout <<
_1 << "*" << _2 << "+" << _3 << "=" <<_1*_2+_3 << "\n";

The placeholders can be used several times in an expression, as shown here. Our function f can now be invoked just like an ordinary function, like so:


f(1,2,3);
f(3,2,1);

The output of running this code follows.


1*2+3=5
3*2+1=7

Any expression where standard operators (the ones that can be overloaded!) are used can be captured in a lambda expression, and stored for later invocation, or passed directly to an algorithm. You will note that when no placeholder is used in a lambda expression (we haven't yet seen how to do that, but it can be done), the result is a nullary function (object). For comparison, when only _1 is used, the result is a unary function object; when just _1 and _2 are used, the result is a binary function object; and when just _1, _2, and _3 are used, the result is a ternary function object. These first lambda expressions have all benefited from the fact that the expression uses only built-in or common C++ operators, which allows coding the algorithms directly. Read on to see how to bind expressions to other functions, class member functions, and even to data members!

BindWhen Operators Aren't Enough


What we've seen so far is great when there are operators available to support our expressions, but that's not always the case. Sometimes, we need to call another function as part of the expression, and that's often referred to as binding; the difference between the binding that we've already seen when creating lambda expressions is that this type of binding requires a separate keyword, bind (hey, that's a clever name!). A bind expression is a delayed function call, either to a free function or a member function. There can be zero or more arguments to the function, some of these can be set directly, some supplied when the function is invoked. With the current version of Boost.Lambda, up to nine arguments are supported (three of which can be applied later through the use of placeholders). To use the binders, you need to include the header "boost/lambda/bind.hpp".

When binding to a function, the first argument is the address of the function, and the subsequent arguments are the arguments. For a non-static class member function, there is always an implicit this argument; in a bind expression, the this argument must be explicitly added. For convenience, the syntax is the same regardless of whether the object is passed by reference or by pointer. So, when binding to a member function, the second argument (that is, the first after the function pointer) is the actual object to which the function should be invoked. It's even possible to bind to data members, which is also demonstrated in the following example:


#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
int main() {
using namespace boost::lambda;
typedef std::map<int,std::string> type;
type keys_and_values;
keys_and_values[3]="Less than pi";
keys_and_values[42]="You tell me";
keys_and_values[0]="Nothing, if you ask me";
std::cout << "What's wrong with the following expression?\n";
std::for_each(
keys_and_values.begin(),
keys_and_values.end(),
std::cout << "key=" <<
bind(&type::value_type::first,_1) << ", value="
<< bind(&type::value_type::second,_1) << '\n');
std::cout << "\n...and why does this work as expected?\n";
std::for_each(
keys_and_values.begin(),
keys_and_values.end(),
std::cout << constant("key=") <<
bind(&type::value_type::first,_1) << ", value="
<< bind(&type::value_type::second,_1) << '\n');
std::cout << '\n';
// Print the size and max_size of the container
(std::cout << "keys_and_values.size()=" <<
bind(&type::size,_1) << "\nkeys_and_values.max_size()="
<< bind(&type::max_size,_1))(keys_and_values);
}

This example starts out with the creation of a std::map with keys of type int and values of type std::string. Remember that the value_type of std::map is a std::pair with the key type and the value type as members. Thus, for our map, the value_type is std::pair<int,std::string>, so in the for_each algorithm, the function object that we pass will receive such a type. Given this pair, it would be nice to be able to extract the two members (the key and the value), and that's exactly what our first bind expression does.


bind(&type::value_type::first,_1)

This expression yields a function object that, when invoked, retrieves the data member first, of the nested type value_type, of its argument, the pair we discussed earlier. In our example, first is the key type of the map, and is thus a const int. This is exactly the same syntax as for member functions. But you'll note that our lambda expression does a bit more; the first part of the expression is


std::cout << "key=" << ...

This compiles, and it works, but it's probably not what's intended. This expression is not a lambda expression; it's just an expression, period. When invoked, it prints key=, but it is only invoked once when the expression is evaluated, not once for each element visited by std::for_each. In the example, the intention is for key= to be the prefix for each key/value pair of our keys_and_values. In earlier examples, we wrote code similar to this, but it didn't exhibit this problem. The reason is that we used a placeholder as the first argument to the operator<<, which made it a valid lambda expression. Here, we must somehow tell Boost.Lambda that it's supposed to create a function object including the "key=". This is done with the function constant, which creates a nullary function object, one that takes no arguments; it merely stores its argument, and then returns it when invoked.


std::cout << constant("key=") << ...

This little change makes all the difference, as shown by the output when running this program.


What's wrong with the following expression?
key=0, value=Nothing, if you ask me
3, value=Less than pi
42, value=You tell me
...and why does this work as expected?
key=0, value=Nothing, if you ask me
key=3, value=Less than pi
key=42, value=You tell me
keys_and_values.size()=3
keys_and_values.max_size()=4294967295

The final part of the example is a binder that binds to a member function rather than a data member; the syntax is identical, and you'll note that in both cases, there's no need to explicitly state the return type of the function. This magic is achieved by automatically deducing the return type of the function or member function, and the type if the binder refers to a data member. However, there is a case where the return type cannot be deduced, and that's when a function object is to be bound; for free functions and member functions, it's a straightforward task to deduce the return type,[2] but for function objects it's impossible. There are two ways around this limitation of the language, and the first is brought forth by the Lambda library itself: overriding the return type deduction by explicitly stating it as a template parameter to the call to bind, as demonstrated by the following program.

[2] Your mileage may wary. Let's just say that it's technically doable.


class double_it {
public:
int operator()(int i) const {
return i*2;
}
};
int main() {
using namespace boost::lambda;
double_it d;
int i=12;
// If you uncomment the following expression,
// the compiler will complain;
// it's just not possible to deduce the return type
// of the function call operator of double_it.
// (std::cout << _1 << "*2=" << (bind(d,_1)))(i);
(std::cout << _1 << "*2=" << (bind<int>(d,_1)))(i);
(std::cout << _1 << "*2=" << (ret<int>(bind(d,_1))))(i);
}

There are two versions of the mechanism that disables the return type deduction systemthe shorthand version is simply passing the return type as a parameter to bind, the second is by using ret, which must enclose any lambda/bind expression where the automatic deduction would otherwise fail. This can quickly become tedious in nested lambda expressions, but there is an even better way, which allows the deduction to succeed. We'll cover that later in this chapter.

Also note that a bind expression can consist of another bind expression, which makes binders a great tool for functional composition. There's plenty of power in nested binds, but tread carefully, because with the power comes additional complexity when reading, writing, and understanding the code.

I Don't Like _1, _2, and _3Can I Rename Them?


Some people aren't comfortable with the predefined placeholder names, so the library offers a convenient way to change them[3] to anything the user wants. This is accomplished by declaring variables of the type boost::lambda::placeholderX_type, where X is 1, 2, or 3. For example, assuming one prefers the names Arg1, Arg2, and Arg3 as names for the placeholders:

[3] Technically, to add new ones.


#include <iostream>
#include <vector>
#include <string>
#include "boost/lambda/lambda.hpp"
boost::lambda::placeholder1_type Arg1;
boost::lambda::placeholder2_type Arg2;
boost::lambda::placeholder3_type Arg3;
template <typename T,typename Operation>
void for_all(T& t,Operation Op) {
std::for_each(t.begin(),t.end(),Op);
}
int main() {
std::vector<std::string> vec;
vec.push_back("What are");
vec.push_back("the names");
vec.push_back("of the");
vec.push_back("placeholders?");
for_all(vec,std::cout << Arg1 << " ");
std::cout << "\nArg1, Arg2, and Arg3!";
}

The placeholder variables you declare this way work just like _1, _2, and _3. As an aside, note the function for_all that is introduced hereit offers a convenient way of avoiding some redundant typing when frequent operations are to be applied to all elements of a containerthat is, when one would typically use for_each. The function accepts two arguments: a reference to a container, and a function or function object. For each element of this container, the element is applied to the function or function objects. I tend to find it quite useful from time to timeperhaps you will too. Running the program produces the following output:


What are the names of the placeholders?
Arg1, Arg2, and Arg3!

Creating your own placeholder names can be a liability for others reading your code; most programmers who know Boost.Lambda (or Boost.Bind) will be familiar with the placeholder names _1, _2, and _3. If you decide to call them q, w, and e, you'll most likely need to explain what they mean to your coworkers. (And you'll probably have to repeat the explanation often!)

I Want to Give My Constants and Variables Names!


Sometimes, the readability of the code can be improved by giving names to constants and variables. As you'll recall, we must sometimes create a lambda expression out of an expression that would otherwise be evaluated immediately. This is done using either constant or var; they operate on constant and mutable variables, respectively. We've already used constant, and var basically works the same way. In complex or long lambda expressions, giving a name to one or more constants can make the expression significantly easier to understand; the same goes for variables. To create named constants and variables, one simply has to define a variable of the type boost::lambda::constant_type<T>::type and boost::lambda::var_type<T>::type, where T is the type of the wrapped constant or variable. Consider this use of a lambda expression:


for_all(vec,
std::cout << constant(' ') << _ << constant('\n'));

It can be rather tedious to use constant all of the time. Following is a sample program that names two constants, newline and space, and uses them in a lambda expression.


#include <iostream>
#include <vector>
#include <algorithm>
#include "boost/lambda/lambda.hpp"
int main() {
using boost::lambda::constant;
using boost::lambda::constant_type;
constant_type<char>::type newline(constant('\n'));
constant_type<char>::type space(constant(' '));
boost::lambda::placeholder1_type _;
std::vector<int> vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
for_all(vec,std::cout << space << _ << newline);
for_all(vec,
std::cout << constant(' ') << _ << constant('\n'));
}

This is a convenient way of avoiding repetitious typing, and making the lambda expressions a little bit clearer. Following is a similar example, which first defines a type memorizer, which keeps track of all the values that have been assigned to it. Then, a named variable is created using var_type, to be used in a subsequent lambda expression. You'll soon see that named constants tend to be needed much more often than named variables, but there are situations where it makes perfect sense to use named variables, too.[4]

[4] Especially when using the lambda looping constructs.


#include <iostream>
#include <vector>
#include <algorithm>
#include "boost/lambda/lambda.hpp"
template <typename T> class memorizer {
std::vector<T> vec_;
public:
memorizer& operator=(const T& t) {
vec_.push_back(t);
return *this;
}
void clear() {
vec_.clear();
}
void report() const {
using boost::lambda::_1;
std::for_each(
vec_.begin(),
vec_.end(),
std::cout << _1 << ",");
}
};
int main() {
using boost::lambda::var_type;
using boost::lambda::var;
using boost::lambda::_1;
std::vector<int> vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
memorizer<int> m;
var_type<memorizer<int> >::type mem(var(m));
std::for_each(vec.begin(),vec.end(),mem=_1);
m.report();
m.clear();
std::for_each(vec.begin(),vec.end(),var(m)=_1);
m.report();
}

That's all there is to it, but before you think that you've got all this nailed down, answer this: What should be the type T in the following declaration?


constant_type<T>::type hello(constant("Hello"));

Is it a char*? A const char*? No, it's actually a constant reference to an array of six characters (the terminating null counts, too), which gives us this:


constant_type<const char (&)[6]>::type
hello(constant("Hello"));

This isn't a pretty sight, and it's a pain for anyone who needs to update the literalwhich is why I find it much cleaner to use the good old std::string to get the job done.


constant_type<std::string>::type
hello_string(constant(std::string("Hello")));

This way, you have to type a little bit more the first time, but you don't need to count the characters, and if there's ever a need to change the string, it just works.

Where Did ptr_fun and mem_fun Go?


Perhaps you've already thought of thisbecause Boost.Lambda creates standard-conforming function objects, there's actually no need to remember the adaptor types from the Standard Library. A lambda expression that binds the function or member function works just as well, and the syntax is the same regardless of the type that's being bound to. This allows the code to stay focused on the task, rather than on some syntactic peculiarity. Here's an example that illustrates these benefits:


#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
void plain_function(int i) {
std::cout << "void plain_function(" << i << ")\n";
}
class some_class {
public:
void member_function(int i) const {
std::cout <<
"void some_class::member_function(" << i << ") const\n";
}
};
int main() {
std::vector<int> vec(3);
vec[0]=12;
vec[1]=10;
vec[2]=7;
some_class sc;
some_class* psc=&sc;
// Bind to a free function using ptr_fun
std::for_each(
vec.begin(),
vec.end(),
std::ptr_fun(plain_function));
// Bind to a member function using mem_fun_ref
std::for_each(vec.begin(),vec.end(),
std::bind1st(
std::mem_fun_ref(&some_class::member_function),sc));
// Bind to a member function using mem_fun
std::for_each(vec.begin(),vec.end(),
std::bind1st(
std::mem_fun(&some_class::member_function),psc));
using namespace boost::lambda;
std::for_each(
vec.begin(),
vec.end(),
bind(&plain_function,_1));
std::for_each(vec.begin(),vec.end(),
bind(&some_class::member_function,sc,_1));
std::for_each(vec.begin(),vec.end(),
bind(&some_class::member_function,psc,_1));
}

There's really no need to make the case for lambda expressions and binders here, is there? Rather than using three different constructs for performing virtually the same thing, we'll let bind figure out what to do, and then be done with it. In the example, it was necessary to use std::bind1st to enable the instance of some_class to be bound to the invocation; with Boost.Lambda, that's part of the job description. So, the next time you are wondering whether to use ptr_fun, mem_fun, or mem_fun_refstop wondering and use Boost.Lambda instead!

Arithmetic Operations Without <functional>


We often perform arithmetic operations on elements from sequences, and the Standard Library helps out by providing a number of binary function objects for arithmetic operations, such as plus, minus, divides, modulus, and so on. However, these function objects require more typing than one likes, and often one argument needs to be bound, which in turn requires the use of binders. When nesting such arithmetic, expressions quickly become unwieldy, and this is yet another area where lambda expressions really shine. Because we are dealing with operators here, both in arithmetic and C++ terms, we have the power to directly code our algorithms as lambda expressions. To give a short motivation, consider the trivial problem of incrementing a numeric value by 4. Then, consider doing that same inside a Standard Library algorithm (such as TRansform). Although the first comes very naturally, the second is a totally different beast (which will drive you into the arms of handwritten loops). Using a lambda expression, focus remains on the arithmetic. In the following example, we'll first use std::bind1st and std::plus to add 4 to each element of a containerand then we'll use lambda to subtract 4.


#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
int main() {
using namespace boost::lambda;
std::vector<int> vec(3);
vec[0]=12;
vec[1]=10;
vec[2]=7;
// Transform using std::bind1st and std::plus
std::transform(vec.begin(),vec.end(),vec.begin(),
std::bind1st(std::plus<int>(),4));
// Transform using a lambda expression
std::transform(vec.begin(),vec.end(),vec.begin(),_1-=4);
}

The difference is astounding! When adding 4 using "traditional" means, it's hard for the untrained eye to see what's going on. Reading the code, we see that we are binding the first argument of a default-constructed instance of std::plus to 4. The lambda expression spells it outsubtract 4 from the element. If you think that the version using bind1st and plus isn't that bad, try it with longer expressions.

Boost.Lambda supports all of the arithmetic operators in C++, so there's rarely a need to include <functional> just for the sake of arithmetic function objects. The following example demonstrates the use of some of those arithmetic operators. Each element in the vector vec is modified using the additive and multiplicative operators.


#include <iostream>
#include <vector>
#include <algorithm>
#include "boost/lambda/lambda.hpp"
int main() {
using namespace boost::lambda;
std::vector<int> vec(3);
vec[0]=1;
vec[1]=2;
vec[2]=3;
std::for_each(vec.begin(),vec.end(),_1+=10);
std::for_each(vec.begin(),vec.end(),_1-=10);
std::for_each(vec.begin(),vec.end(),_1*=3);
std::for_each(vec.begin(),vec.end(),_1/=2);
std::for_each(vec.begin(),vec.end(),_1%=3);
}

Terse, readable, and maintainablethat's the kind of code you get with Boost.Lambda. Skip std::plus, std::minus, std::multiplies, std::divides, and std::modulus; your code is always better with Boost.Lambda.

Writing Readable Predicates


Many of the algorithms in the Standard Library come in a version that accepts a unary or binary predicate. These predicates are free functions of function objects, but of course, a lambda expression also fits the bill. For predicates that are used often, it makes perfect sense to define function objects, but frequently, they are used once or twice and then never looked at again. In such cases, a lambda expression is a superior choice, both because the code becomes easier to understand (all functionality resides at the same location), and because the code isn't cluttered with function objects that are rarely used. As a concrete example, consider finding an element with a specific value in a container. If operator== is defined for the type, it's easy to use the algorithm find directly, but what if another criteria for the search is to be used? Given the type search_for_me in the following, how would you use find to search for the first element where the member function a returns "apple"?


#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
class search_for_me {
std::string a_;
std::string b_;
public:
search_for_me() {}
search_for_me(const std::string& a,const std::string& b)
: a_(a),b_(b) {}
std::string a() const {
return a_;
}
std::string b() const {
return b_;
}
};
int main() {
std::vector<search_for_me> vec;
vec.push_back(search_for_me("apple","banana"));
vec.push_back(search_for_me("orange","mango"));
std::vector<search_for_me>::iterator it=
std::find_if(vec.begin(),vec.end(),???);
if (it!=vec.end())
std::cout << it->a() << '\n';
}

First of all, note that we need to use find_if,[5] but how should the predicate marked with ??? in the preceding code be defined? Here's one way: a function object that implements the logic for the predicate.

[5] find uses operator==; find_if requires an additional predicate function (or function object).


class a_finder {
std::string val_;
public:
a_finder() {}
a_finder(const std::string& val) : val_(val) {}
bool operator()(const search_for_me& s) const {
return s.a()==val_;
}
};

This function object can be used like so:


std::vector<search_for_me>::iterator it=
std::find_if(vec.begin(),vec.end(),a_finder("apple"));

That's fine, but two minutes (or days) later, we'll want another function object, this time one that tests the member function b. And so on…this sort of thing quickly becomes tedious. As you've no doubt guessed, this is another excellent case for lambda expressions; we need the flexibility of creating the predicate directly where and when it's needed. The preceding find_if could have been written like this.


std::vector<search_for_me>::iterator it=
std::find_if(vec.begin(),vec.end(),
bind(&search_for_me::a,_1)=="apple");

We bind to the member function a, and we test it for equality with "apple" and that's our unary predicate in full, defined right where it's used. But wait, as they say, there's more. When dealing with numeric types, we have the full range of arithmetic operators, comparisons, and logical operations to choose from. This means that even complex predicates are straightforward to define. Read the following code carefully, and see how well the predicates are expressed.


#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include "boost/lambda/lambda.hpp"
int main() {
using namespace boost::lambda;
std::vector<int> vec1;
vec1.push_back(2);
vec1.push_back(3);
vec1.push_back(5);
vec1.push_back(7);
vec1.push_back(11);
std::vector<int> vec2;
vec2.push_back(7);
vec2.push_back(4);
vec2.push_back(2);
vec2.push_back(3);
vec2.push_back(1);
std::cout << *std::find_if(vec1.begin(),vec1.end(),
(_1>=3 && _1<5) || _1<1) << '\n';
std::cout << *std::find_if(vec2.begin(),vec2.end(),
_1>=4 && _1<10) << '\n';
std::cout << *std::find_if(vec1.begin(),vec1.end(),
_1==4 || _1==5) << '\n';
std::cout << *std::find_if(vec2.begin(),vec2.end(),
_1!=7 && _1<10) << '\n';
std::cout << *std::find_if(vec1.begin(),vec1.end(),
!(_1%3)) << '\n';
std::cout << *std::find_if(vec2.begin(),vec2.end(),
_1/2<3) << '\n';
}

As you can see, creating such predicates is as easy as writing down the logic in the first place. This is one of my favorite uses for lambda expressions, because they can be understood by just about anyone. It's inevitable that we sometimes need to choose other mechanisms than lambda expressions simply because of the competence profiles of those who must understand the code; but here, there's nothing but added value.

Make Your Function Objects Play Nicely with Boost.Lambda


Not all expressions are suitable as lambda expressionscomplex expressions are better suited for regular function objects, and expressions that are reused as-is many times should also be made first-class citizens of your code base. They should be collected in a library of reusable function objects. But, you'll likely want to use these function objects in lambda expressions, too, and you'll want them to play nicely with Lambda; not all function objects do. The problem is that the return type of function objects cannot be deduced the way ordinary functions can; this is an inherent limitation of the language. However, there is a well-defined way for providing this important information to the Lambda library, which in turn makes bind expressions much cleaner. To give an example of the problem, consider the following function object:


template <typename T> class add_prev {
T prev_;
public:
T operator()(T t) {
prev_+=t;
return prev_;
}
};

Given such a function object, a lambda expression cannot deduce the return type, so the following example doesn't compile.


#include <iostream>
#include <algorithm>
#include <vector>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
int main() {
using namespace boost::lambda;
std::vector<int> vec;
vec.push_back(5);
vec.push_back(8);
vec.push_back(2);
vec.push_back(1);
add_prev<int> ap;
std::transform(
vec.begin(),
vec.end(),
vec.begin(),
bind(var(ap),_1));
}

The problem is the call to transform.


std::transform(vec.begin(),vec.end(),vec.begin(),bind(var(ap),_1));

When the binder is instantiated, the mechanism for return type deduction kicks in…and fails. Thus, the program does not compile, and you must explicitly tell bind the return type, like so:


std::transform(vec.begin(),vec.end(),vec.begin(),
bind<int>(var(ap),_1));

This is a shorthand notation for the general form of explicitly setting the return types for lambda expression, and it's the equivalent of this code.


std::transform(vec.begin(),vec.end(),vec.begin(),
ret<int>(bind<int>(var(ap),_1)));

This problem isn't new; it's virtually the same that applies for function objects used by the Standard Library algorithms. There, the solution is to add typedefs that state the return type and argument type(s) of the function objects. The Standard Library even provides helper classes for accomplishing this, through the class templates unary_function and binary_functionour example class add_prev could become a compliant function object by either defining the required typedefs (argument_type and result_type for unary function objects, first_argument_type, second_argument_type, and result_type for binary function objects), or inheriting from unary_function/binary_function.


template <typename T> class add_prev : public std::unary_function<T,T>

Is this good enough for lambda expressions, too? Can we simply reuse this scheme, and thus our existing function objects, too? Alas, the answer is no. There is a problem with this typedef approach: What happens when the result type or the argument type(s) is dependent on a template parameter to a parameterized function call operator? Or, when there are several overloaded function call operators? Had there been language support for template typedefs, much of the problem would be solved, but currently, that's not the case. That's why Boost.Lambda requires a different approach, through a nested parameterized class called sig. To enable the return type deduction to work with add_prev, we must define a nested type sig like this:


template <typename T> class add_prev :
public std::unary_function<T,T> {
T prev_;
public:
template <typename Args> class sig {
public:
typedef T type;
};
// Rest of definition

The template parameter Args is actually a tuple containing the function object (first element) and the types of the arguments to the function call operator. In our case, we have no need for this information, as the return type and the argument type are always T. Using this improved version of add_prev, there's no need to short-circuit the return type deduction in a lambda expression, so our original version of the code now compiles cleanly.


std::transform(vec.begin(),vec.end(),vec.begin(),bind(var(ap),_1));

To see how the tuple in the template parameter to sig works, consider another function object with two function call operators, one version accepting an int argument, the other accepting a reference to const std::string. The problem that we need to solve can be expressed as, "if the second element of the tuple passed to the sig template is of type int, set the return type to std::string; if the second element of the tuple passed to the sig template is of type std::string, set the return type to double." To do this, we'll add another class template that we can specialize and then use in add_prev::sig.


template <typename T> class sig_helper {};
// The version for the overload on int
template<> class sig_helper<int> {
public:
typedef std::string type;
};
// The version for the overload on std::string
template<> class sig_helper<std::string> {
public:
typedef double type;
};
// The function object
class some_function_object {
template <typename Args> class sig {
typedef typename boost::tuples::element<1,Args>::type
cv_first_argument_type;
typedef typename
boost::remove_cv<cv_first_argument_type>::type
first_argument_type;
public:
// The first argument helps us decide the correct version
typedef typename
sig_helper<first_argument_type>::type type;
};
std::string operator()(int i) const {
std::cout << i << '\n';
return "Hello!";
}
double operator()(const std::string& s) const {
std::cout << s << '\n';
return 3.14159265353;
}
};

There are two important parts to study herefirst, the helper class sig_helper, which is class parameterized on a type T. This type is either int or std::string, depending on which of the overloaded versions of the function call operator is requested. By fully specializing this template, the correct typedef, type, is defined. The next interesting part is the sig class, where the first argument type (the second element of the tuple) is retrieved, any const or volatile qualifiers are removed, and the resulting type is used to instantiate the correct version of the sig_helper class, which has the correct typedef type. This is a rather complex (but necessary!) way of defining the return types for our classes, but most of the time, there's only one version of the function call operator; and then it's a trivial task to correctly add the nested sig class.

It's important that our function objects work without hassle in lambda expressions, and defining the nested sig class where it's needed is definitely a good idea; it helps a lot.

Control Structures in Lambda Expressions


We have seen that powerful lambda expressions can be created with ease, but many programming problems require that we be able to express conditions, which we do in C++ using if-then-else, for, while, and so on. There are lambda versions of all C++ control structures in Boost.Lambda. To use the selection statements, if and switch, include the files "boost/lambda/if.hpp" and "boost/lambda/switch.hpp", respectively. For the iteration statements, while, do, and for, include "boost/lambda/loops.hpp". It's not possible to overload keywords, so the syntax is slightly different than what you're used to, but the correlation is obvious. As a first example, we'll see how to create a simple if-then-else construct in a lambda expression. The form is if_then_else(condition, then-statements, else-statements). There is also another syntactic form, which has the form if_(condition)[then-statements].else_[else-statements].


#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
#include "boost/lambda/if.hpp"
int main() {
using namespace boost::lambda;
std::vector<std::string> vec;
vec.push_back("Lambda");
vec.push_back("expressions");
vec.push_back("really");
vec.push_back("rock");
std::for_each(vec.begin(),vec.end(),if_then_else(
bind(&std::string::size,_1)<=6u,
std::cout << _1 << '\n',
std::cout << constant("Skip.\n")));
std::for_each(vec.begin(),vec.end(),
if_(bind(&std::string::size,_1)<=6u) [
std::cout << _1 << '\n'
]
.else_[
std::cout << constant("Skip.\n")
] );
}

If you've read the whole chapter up to now, you probably find the preceding example quite readable; if you're jumping right in, this is probably a scary read. Control structures immediately add to the complexity of reading lambda expressions, so it does take a little longer to get used to. After you get the hang of it, it comes naturally (the same goes for writing them!). Deciding which syntactic form to use is merely a matter of taste; they do exactly the same thing.

In the preceding example, we have a vector of strings and, if their size is less than or equal to 6, they are printed to std::cout; otherwise, the string "Skip" is printed. There are a few things worth noting in the if_then_else expression.


if_then_else(
bind(&std::string::size,_1)<=6u,
std::cout << _1 << '\n',
std::cout << constant("Skip.\n")));

First, the condition is a predicate, and it must be a lambda expression! Second, the then-statement must be a lambda expression! Third, the else-statement must beget readya lambda expression! The first two come naturally in this case, but it's easy to forget the constant to make the string literal ("Skip\n") a lambda expression. The observant reader notices that the example uses 6u, and not simply 6, to make sure that the comparison is performed using two unsigned types. The reason for this is that we're dealing with deeply nested templates, which means that when a lambda expression like this happens to trigger a compiler warning, the output is really, really long-winded. Try removing the u in the example and see how your compiler likes it! You should see a warning about comparing signed and unsigned types because std::string::size returns an unsigned type.

The return type of the control structures is void, with the exception of if_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 then-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
(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 substatement 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 large 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";
}
};
int main() {
using namespace boost::lambda;
base* p1=new base;
base* p2=new derived;
derived* pd=0;
(if_(var(pd)=ll_dynamic_cast<derived*>(_1))
[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 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."

The most interesting part is where the exception is thrown.


throw_exception(
bind(constructor<some_exception>(),
constant("Somewhere, something went terribly wrong."))

The argument to throw_exception is a lambda expression. In this case, it is created by binding a call to the some_exception constructor, to which we pass the what argument, a string literal.

That's all there is to exception handling in Boost.Lambda. As always, remember to treat these tools with care and respect, as they can make life easier or harder depending on how well and judiciously you utilize them.[10] Throwing and handling exceptions should not be common in your lambda expressions, but it's necessary and reasonable from time to time.

[10] Beware the tendency to fulfill the old adage, "When all you have is a hammer, everything looks like a nail."

/ 124