Binders |
There are times when it is desireable to bind a simple functor, function, member function or member variable for deferred evaluation. This can be done through the binding facilities provided below. There are template classes:
These template classes are specialized lazy function classes for functors, function pointers, member function pointers and member variable pointers, respectively. These are subclasses of the lazy- function class (see functions). Each of these has a corresponding overloaded bind(x) function. Each bind(x) function generates a suitable binder object.
Example, given a function foo:
void foo_(int n) { std::cout << n << std::endl; }
Here's how the function foo is bound:
bind(&foo_)
This bind expression results to a lazy-function (see functions) that is lazily evaluated. This bind expression is also equivalent to:
function_ptr<void, int> foo = &foo_;
The template parameter of the function_ptr is the return and argument types of actual signature of the function to be bound read from left to right. Examples:
void foo_(int); ---> function_ptr<void, int>
int bar_(double, int); ---> function_ptr<int, double, int>
Either bind(&foo_) and its equivalent foo can now be used in the same way a lazy function (see functions) is used:
bind(&foo_)(arg1)
or
foo(arg1)
The latter, of course, follows C/C++ function call syntax and is much easier to understand. This is now a full-fledged lazy function that can finally be evaluated by another function call invocation. A second function call will invoke the actual foo function:
int i = 4;
foo(arg1)(i);
will print out "4".
Binding functors and member functions can be done similarly. Here's how to bind a functor (e.g. std::plus<int>):
bind(std::plus<int>())
or
functor<std::plus<int> > plus;
Again, these are full-fledged lazy functions. In this case, unlike the first example, expect 2 arguments (std::plus<int> needs two arguments lhs and rhs). Either or both of which can be lazily bound:
plus(arg1, arg2) // arg1 + arg2
plus(100, arg1) // 100 + arg1
plus(100, 200) // 300
A bound member function takes in a pointer or reference to an object as the first argument. For instance, given:
struct xyz { void foo(int) const; };
xyz's foo member function can be bound as:
bind(&xyz::foo)
or
member_function_ptr<void, xyz, int> xyz_foo = &xyz::foo;
The template parameter of the member_function_ptr is the return, class and argument types of actual signature of the function to be bound, read from left to right:
void xyz::foo_(int); ---> member_function_ptr<void, xyz, int>
int abc::bar_(double, char); ---> member_function_ptr<int, abc, double, char>
Take note that a member_function_ptr lazy-function expects the first argument to be a pointer or reference to an object. Both the object (reference or pointer) and the arguments can be lazily bound. Examples:
xyz obj;
xyz_foo(arg1, arg2) // arg1.foo(arg2)
xyz_foo(obj, arg1) // obj.foo(arg1)
xyz_foo(obj, 100) // obj.foo(100)
Be reminded that var(obj) must be used to call non-const member functions. For example, if xyz was declared as:
struct xyz { void foo(int); }; // note non-const member function
the pointer or reference to the object must also be non-const since lazily bound arguments are stored as const value by default (see variable class in primitives).
xyz_foo(var(obj), 100) // obj.foo(100)
arg1..argN are already implicitly mutable. There is no need to wrap arg1..argN in a var. It is an error to do so:
var(arg1) // ERROR! arg1 is already mutable
var(arg2) // ERROR! arg2 is already mutable
Finally, member variables can be bound much like member functions. For instance, given:
struct xyz { int v; };
xyz::v can be bound as:
bind(&xyz::v)
or
member_var_ptr<int, xyz> xyz_v = &xyz::v;
The template parameter of the member_var_ptr is the type of the variable followed by the class:
int xyz::v; ---> member_var_ptr<int, xyz>
Just like the member_function_ptr, member_var_ptr also expects the first argument to be a pointer or reference to an object. Both the object (reference or pointer) and the arguments can be lazily bound. Like member function binders, var(obj) must be used to access non-const member variables. Examples:
xyz obj;
xyz_v(arg1) // arg1.v (const& access)
xyz_v(obj) // obj.v (const& access)
xyz_v(var(obj))() = 3 // obj.v = 3 (non-const& access)
xyz_v(arg1)(obj) = 4 // obj.v = 4 (non-const& access)
Be reminded once more that binders are monomorphic. This layer is provided only for compatibility with existing code such as prewritten STL functors and legacy APIs. Rather than binding functions or functors, the preferred method is to write true generic and polymorphic lazy-functions (see functions). However, since most of the time we are dealing with adaptation of exisiting code, binders are indeed indespensible.
Copyright © 2001-2002 Joel de Guzman
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)