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.. To use the binders, y
Trang 1function 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(),
Trang 2std::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
Trang 3function 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;
}
};
Trang 4int 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"
Trang 5boost::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
Trang 6This 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