Chapter 10. The bind Function Template

One Ring to bring them all and in the darkness bind them.

The Fellowship of the Ring
J.R.R. TOLKIEN

Suppose that you have a pair of pointers, first and last, defining a sequence of integer values, and you need to know how many elements in the sequence have a value that is greater than or equal to 10. You can do this with the standard algorithm count_if and a predicate that returns true when called with a value that is greater than or equal to 10. The standard library doesn’t have that predicate, but it has less, which takes two arguments and returns true if the first argument is less than the second. If you had a way to tell count_if to always use 10 as the first argument to the less predicate, you’d have the problem solved. That’s a simple example of what the TR1 template function bind can do.

Example 10.1. Simple bind Example (funobjbind/simple.cpp)


#include <functional>
#include <iostream>
using std::tr1::bind;
using namespace std::tr1::placeholders;
using std::less; using std::cout;

template <class Pr>
int count_elements(const int *first, const int *last,
  Pr pred)
  { // count elements in the range [first, last) for which pred(elt) is true
  int count = 0;
  while (first != last)
    if (pred(*first++))
      ++count;
  return  count;
  }

int count_ge10(int *first, int *last)
  { // bind 10 as first argument to less<int>
  int val = 10;
  return count_elements(first, last,


    bind(less<int>(), val, _1));
  }

int main()
  { // demonstrate simple uses of bind
  int values[6] = { 1, 3, 19, 12, 6, 11 };
  int count = count_ge10(values, values + 6);
  cout << count
    << "values greater than or equal to 10 ";
  return 0;
  }


The function count_ge10 calls bind with three arguments. The first is the target object. Its type is less<int>, and it has a function call operator that takes two arguments of type int. The second argument is val, with a value of 10. Because it’s the first argument after the target object, it tells bind what the first argument to the target object should be. When the object returned by bind is called, the value val will be passed as the first argument to the target object. The third argument is the placeholder _1. Because it’s the second argument after the target object, it tells bind what the second argument to the target object should be. The placeholder _1 says that this argument should be the value passed as the first argument when the bind object is called. The resulting bind object is then passed as the predicate to the function template count_elements.[1]

The function template count_elements steps through the elements of its input range, passing each element in turn to the predicate pred and incrementing count each time the call to pred returns true. In this example, the call to pred(*first++) does the same thing as this code:

less<int> pr;
pr(10, *first++);

That is, the call to bind has bound the value 10 as the first argument to less<int>, producing a predicate that can be called with a single argument of type int that returns true if the value of its argument is greater than or equal to 10.

The function template bind takes a callable object as its first argument and returns a call wrapper whose target object is a copy of the callable object. The function template also takes additional arguments that describe the arguments that will be passed to the target object when it is called. When the additional argument is a value, that value is passed when the target object is called. When the additional argument is a placeholder, the value passed to the bind object’s function call operator is passed to the target object. This lets you turn a function object that takes two arguments into a function object that takes one argument, with a fixed value for its other argument.

10.1. Placeholders

namespace std {
  namespace tr1 {
    namespace placeholders {
    extern unspecified _1;
    extern unspecified _2;
    extern unspecified _3;
} } }

Placeholder types have default constructors and copy constructors that do not throw exceptions. The types are not required to support assignment, but if they do, their assignment operators do not throw exceptions.

As we’ll see in the next section, placeholders are passed as arguments to the bind function template to designate argument values that will be supplied later. The placeholders supplied in the TR1 library are named _1, _2, and so on, up to the maximum value supported by the implementation. You can also supply your own placeholders, although you’ll probably never need to do this. If you do, see Section 10.3 for an explanation of how to do it.

10.2. unspecified bind(…..)

template <class Fty, class T1, class T2, …, class TN>
  unspecified bind(Fty fn, T1 t1, T2 t2, …, TN tn);
template <class Ret,
  class Fty, class T1, class T2, …, class TN>
  unspecified bind(Fty fn, T1 t1, T2 t2, …, TN tn);

The first function returns a forwarding call wrapper (see Section 6.1) wrap that has a weak result type (see Section 6.2). Calling wrap(u1, u2, …, uM) returns INVOKE_R (fn, v1, v2, …, vn, Ret), where cv represents the cv-qualifiers of wrap, the values v1, v2, …, vN and their types V1, V2, …, VN are determined by the rules for bound types, and Ret is the type named by result_of<Fty cv (V1, V2, …, VN)>::type).

The second function returns a forwarding call wrapper (see Section 6.1) wrap that has a nested type named result_type that is a synonym for the template argument Ret. Calling wrap(u1, u2, …, uM) returns INVOKE_R (fn, v1, v2, …, vn, Ret), where cv represents the cv-qualifiers of wrap, and the values v1, v2, …, vN and their types V1, V2, …, VN are determined by the rules for bound types.

For both functions, the types Fty, T1, …, TN must be copy constructible, and a set of values w1, w2, …, wN must exist for which INVOKE (fn, w1, w2, …, wN) is a valid expression.

The bound arguments v1, v2, …, vN and their types V1, V2, …, VN are determined by the type of the corresponding argument ti and its type Ti in the call to bind and by the cv-qualifiers cv of the call wrapper wrap. The value and type of the argument vi are determined by the first of the following four rules that applies.

1. If the type of ti is reference_wrapper<T> for some T, the argument vi is ti.get(), and its type Vi is T&.

2. If the value of std::tr1::is_bind_expression<Ti>::value is true, the argument vi is ti(u1, u2, …, uM), and its type Vi is result_of<Ticv (U1&, U2&, …, UM&)>::type.

3. If the value j of std::tr1::is_placeholder<Ti>::value is not zero, the argument vi is uj, and its type Vi is Uj&.

4. Otherwise, the argument vi is ti, and its type Vi is cv Ti&.

That’s pretty dense. To sort it out, we’ll look at the additional arguments to bind, then start at the bottom of the list of rules and work our way up to the top. Keep in mind that three calls and three corresponding argument lists are involved. First is a call to bind, which returns a call wrapper; second is a call to that call wrapper, which provides additional arguments that can be referred to by placeholder arguments in the call to bind; and third is a call to the call wrapper’s target object, made by the call wrapper. The types of the arguments in the call to bind determine how those arguments are treated and, sometimes, how the arguments in the call to the call wrapper are treated.

10.2.1. Additional Arguments to bind

Every call to bind must have one argument that is a callable object and as many additional arguments as are needed to call that callable object.

Example 10.2. Additional Arguments (funobjbind/additional.cpp)


#include <functional>
using std::tr1::bind;

int no_args()
  { // function taking no arguments
  return 1;
  };

struct one_arg
  { // class type with member operator() taking one argument
  int operator()(int i) const
    { // function call operator that takes one argument
    return i;
    }
  typedef int result_type;
  };

struct three_args
  { // class type with member function taking two arguments
  int f(int i, int j) const
    {  // member function taking two arguments
    return i + j;
    }
  };

void call_bind()
  { // examples of calls to bind
  // no additional arguments
  bind(no_args);

  // one additional argument
  one_arg a1;
  bind(a1, 1);

  // three additional arguments


  three_args a3;
  bind(&three_args::f, a3, 1, 2);
  }


In this example, the first call to bind passes a single argument that is a pointer to the function no_args. Because no_args takes no arguments, the call to bind is made with no additional arguments. The second call to bind passes a callable object of type one_arg. Since one_arg’s function call operator takes one argument, the call to bind has one additional argument. The third call to bind passes an argument that is a pointer to a member function of the class three_args. In order to call the member function, we need one argument that designates the object for the member function and as many more additional arguments as there are arguments to the member function. Since three_-args::f takes two arguments, the call to bind must have three additional arguments: the object and the two arguments for three_args::f.

10.2.2. Ordinary Arguments

Under rule 4, arguments that are ordinary types are simply passed to the target object when the bind object is called.

Example 10.3. Rule 4 (funobjbind/rule4.cpp)


#include <iostream>
#include <functional>
using std::tr1::bind;
using std::cout;

int no_args()
  {   // function taking no arguments
  return 0;
  };

struct one_arg
  {   // class type with member operator() taking one argument
  int operator()(int i) const
    {   // function call operator that takes one argument
    return i;
    }
  typedef int result_type;
  };

struct three_args
  {   // class type with member function taking two arguments
  three_args(int v) : val(v) {}

  int f(int i, int j) const
    {    // member function taking two arguments
    return val + 2 * i + 3 * j;
    }
private :
  int val;
  };

int main()
  {   // examples of calls to bind
  // no additional arguments
  cout << bind(no_args)() << ' ';
  cout << no_args() << ' ';    // equivalent call

  // one additional argument
  one_arg a1;
  cout << bind(a1, 1)() << ' ';
  cout << a1(1) << ' ';        // equivalent call

  // three additional arguments
  three_args a3(1);
  cout << bind(&three_args::f, a3, 2, 3)() << ' ';
  cout << a3.f(2, 3) << ' ';   // equivalent call
  return 0;
  }


In this example, the three calls to bind are the same as the calls in the previous example. The main difference here is that the object returned by each of those calls to bind is then called with no arguments. Calling the returned object in turn calls the target object, passing the arguments given in the original call to bind.

The first call to bind passes the function pointer no_args. The returned object holds a copy of that pointer. Calling the returned object simply calls no_args, which returns the value 0. The returned object’s function call operator returns that value, so the program displays the value 0 for this operation.

The second call to bind passes a callable object of type one_arg and the value 1. The returned object holds a copy of the callable object and of the additional argument: in this case, 1. Calling the returned object in turn calls the target object of type one_arg with the stored value 1. The call to the target object returns its argument, and the call of the returned object also returns that value, so the program displays the value 1 for this operation.

The third call to bind passes a pointer to member function, &three_args::f, followed by an object of type three_args and the values 2 and 3. The returned object holds a copy of each of those four arguments. Calling the returned object calls a.f(2, 3), where a is the stored copy of the object of type three_args. That member function call returns the value 14, so the call of the returned object also returns 14. The program displays the value 14 for this operation.

10.2.3. Placeholder Arguments

Under rule 3, if std::tr1::is_placeholder<Ti>::value is not zero, the value passed to the target object as the argument at position i is the value passed in the call to the bind object as the argument at position is_place-holder<Ti>::value. For the standard placeholders _1, _2, and so on, the corresponding values of is_placeholder::value are 1, 2, and so on. So the placeholder _1 says to pass the first argument, _2 says to pass the second argument, and so on. For example:

bind(f, _1)(a, b)        // calls f(a)
bind(f, _2)(a, b)        // calls f(b)
bind(g, _1, _2)(a, b)    // calls g(a, b)
bind(g, _2, _1)(a, b)    // calls g(b, a)
bind(g, c, _1)(a, b)     // calls g(c, a)

In real code:

Example 10.4. Rule 3 (funobjbind/rule3.cpp)


#include <iostream>
#include <functional>
using std::tr1::bind;
using namespace std::tr1::placeholders;
using std::cout;

struct one_arg
  { // class type with member operator() taking one argument
  int operator()(int i) const
    { // function call operator that takes one argument
    return i;
    }
  typedef int result_type;
  };

struct three_args
  { // class type with member function taking two arguments
  three_args(int v) : val(v) {}
  int f(int i, int j) const
    {   // member function taking two arguments

    return val + 2 * i + 3 * j;
    }
private :
  int val;
  };

int main()
  { // examples of calls to bind
  // argument values
  int a = 10;
  int b = 11;
  int c = 12;

  // one additional argument
  one_arg a1;
  cout << bind(a1, _1)(a, b, c) << ' ';
  cout << a1(a) << ' ';          // equivalent call
  cout << bind(a1, _2)(a, b, c) << ' ';
  cout << a1(b) << ' ';          // equivalent call
  cout << bind(a1, _3)(a, b, c) << ' ';
  cout << a1(c) << ' ';          // equivalent call
  cout << bind(a1, 10)( a, b, c) << ' ';
  cout << a1(10) << ' ';         // equivalent call

  // three additional arguments
  three_args a3(1);
  cout << bind(&three_args::f, a3, _1, _2)(a, b, c)
    << ' ';
  cout << a3.f(a, b) << ' ';     // equivalent call
  cout << bind(&three_args::f, a3, _2, _3)(a, b, c)
    << ' ';
  cout << a3.f(b, c) << ' ';     // equivalent call
  cout << bind(&three_args::f, a3, _3, _2)(a, b, c)
    << ' ';
  cout << a3.f(c, b) << ' ';     // equivalent call
  cout << bind(&three_args::f, a3, 1, _2)(a, b, c)
    << ' ';
  cout << a3.f(1, b) << ' ';     // equivalent call
  return 0;
  }


You probably noticed in both of the preceding code examples that the call to the object returned by bind used named variables, not literals. That’s because rule 3 says that the declared type of the argument to the function call operator is Uj&. In this example, all the Ujs are of type int, so the argument types are all int&s. That’s fine for named variables of type int but leads to a problem for literals, because you can’t pass a literal value where an int& is expected.[2] This is an example of what is known as the forwarding problem: It’s very difficult to write a function template that passes its arguments by reference to another function. The problem arises because template arguments of type T and of type const T are both treated by the compiler as T. Ordinarily, that’s what you want: You should be able to call a function that takes an argument of type int with the value 1, even though the type of 1 is const int. But when you want to use a reference to the type, const matters. The rule in the TR1 library is that you get a non-const reference, and there’s a suggestion that implementations ought to try to support const references as well. Unfortunately, doing so in standard C++ requires multiple overloads:

void f(int&, int&);
void f(const int&, int&);
void f(int&, const int&);
void f(const int&, const int&);

This, in turn, requires multiple function templates, one for each combination of const and non-const arguments. The number of overloads needed is 2n, where n is the number of arguments. Obviously, this becomes prohibitive as n becomes larger. So don’t count on being able to call bind objects with constant values.[3]

10.2.4. bind Arguments

Under rule 2, arguments to bind can themselves be objects returned by calls to bind. For example:

bind(f, bind(g, a), b)(c, d)    // calls f(g(a), b)
bind(f, bind(g, b), _1)(c, d)   // calls f(g(b), c)

Any placeholder arguments in nested calls to bind refer to the arguments in the innermost call to a bind object that contains the placeholder. For example:

bind(f, bind(g, _1), a)(c, d)             // calls f(g(c), a)
bind(f, bind(g, _1), _2)(c, d)            // calls f(g(c), d)
bind (f, bind(g, _2)(a, b), _2)(c, d)     // calls f(g(b), d)

In real code:

Example 10.5. Rule 2 (funobjbind/rule2.cpp)


#include <iostream>
#include <functional>
using std::tr1::bind;
using namespace std::tr1::placeholders;
using std::cout;

struct one_arg
  { // class type with member operator() taking one argument
  int operator()(int i) const
    { // function call operator that takes one argument
    return i;
    }
  typedef int result_type;
  };

struct three_args
  { // class type with member function taking two arguments
  three_args(int v) : val(v) {}
  int f(int i, int j) const
    {   // member function taking two arguments
    return val + 2 * i + 3 * j;
    }
private :
  int val;
  };

int main()
  { // examples of calls to bind
  // argument values
  int a = 10;
  int b = 11;
  int c = 12;
  one_arg a1;
  one_arg a2;
  three_args a3(2);

  // no additional arguments
  cout << bind(a1, bind(a2, a))() << ' ';
  cout << a1(a2(a)) << ' ';            // equivalent call

  // one additional argument
  cout << bind(a1, bind(a2, _1))(b) << ' ';
  cout << a1(a2(b)) << ' ';            // equivalent call

  // two additional arguments
  cout << bind(a1, bind(a2, _1))(a, b) << ' ';
  cout << a1(a2(a)) << ' ';             // equivalent call
  cout << bind(&three_args::f, a3,
    bind(a1, _1), bind(a2, _1))(a, b) << ' ';
  cout << a3.f(a1(a), a2(a)) << ' ';    // equivalent call

  return 0;
  }


10.2.5. reference_wrapper Arguments

Under rule 1, arguments to bind can be objects of type reference_wrap-per<Ty>. In this case, the argument passed to the target object is passed by reference rather than by value.

Example 10.6. Rule 1 (funobjbind/rule1.cpp)


#include <functional>
#include <iostream>
using std::tr1::bind; using std::tr1::ref;
using std::cout;

void modify(int& arg)
  {  // add 1 to argument
  ++arg;
  }

int main()
  {   // demonstrate use of reference_wrapper with bind
  int i = 0;
  cout << i << ' ';

  // i passed by value; not modified
  bind(modify, i)();
  cout << i << ' ';

  // i passed by reference; modified
  bind(modify, ref(i))();

  cout << i << ' ';
  return 0;
  }


In the first call to bind, the argument i is passed directly. Its value is copied into the callable object returned by bind, and that copy is passed to modify when the callable object is called. Because modify is passed a copy of i, the original value of i is not changed.

In the second call to bind, the argument i is wrapped in a reference_-wrapper object by the call to ref. The callable object returned by bind holds a reference to i, and that reference is passed in the call to modify. The value of i is changed by the call to modify.

10.3. Extending bind

template <class Ty> struct is_placeholder {
  static const int value;
  };
template <class Ty> struct is_bind_expression {
  static const bool value;
  };

As mentioned earlier, bind determines whether an argument of type Arg is a placeholder by examining is_placeholder<Arg>::value. If its value is zero, the argument is not a placeholder; if its value is nonzero the value indicates which argument the placeholder refers to. Similarly, is_bind_expression<Arg>::value is true if Arg is a bind expression.

These templates can be specialized for types defined in your code.[4] In particular, if you’re using another template library that binds arguments to callable types, you can pass that library’s objects and placeholders to bind if you’ve provided specializations of is_placeholder and is_bind_expression for that library’s types. The call to bind and to the object it returns will then follow rules 2 and 3.

Exercises

Exercise 1

For each of the following errors, write a simple test case containing the error, and try to compile it. In the error messages, look for the key words that relate to the error in the code.

1. Calling bind with a placeholder argument that tries to use an argument that is past the implementation’s maximum allowed value.

2. Calling bind with fewer or more arguments than the callable object takes. (You may have to call the returned object to get an error message.)

3. Calling an object returned by bind with a literal value instead of an lvalue.

4. Calling bind with a first argument that is not a callable object. (You may have to call the returned object to get an error message.)

Exercise 2

Write a program that calls bind, passing a pointer to the standard library function cosf and the value 1.0f, and calls the resulting callable object with no arguments. Show the result.

Exercise 3

Write a program that calls bind, passing a pointer to the standard library function cosf and the placeholder _1, and calls the resulting callable object with an argument that is a variable of type float holding the value 1.0f.

Exercise 4

Write a program that calls bind, passing a pointer to the standard library function cosf and the placeholder _2, and calls the resulting callable object with two arguments. The second argument should be a variable of type float holding the value 1.0f.

Exercise 5

Write a program that defines a type named employee with a public member function named ID that takes no arguments and returns a value of type int. The constructor for employee should set the value that will be returned by ID. Create a callable object whose function call operator returns true for all employee objects whose ID is greater than 10. This takes two steps: Call bind to bind the member function ID to the employee object that will be passed to the function call operator (use _1 to refer to that object), and call bind again to pass that result and the value 10 to the standard library class template less. Create a TR1 array of employee objects, and use the standard algorithm count_if to count the number of employees whose ID is greater than 10.

Exercise 6

Using the employee type from the previous example, write a program that sorts a standard library vector object that holds employee objects so that the contained objects are in order according to their ID values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.186.224