Files
Purpose
boost::bind is a generalization of the standard functions
std::bind1st and std::bind2nd. It supports arbitrary function
objects, functions, function pointers, and member function pointers, and is able
to bind any argument to a specific value or route input arguments into arbitrary
positions. bind does not place any requirements on the function object;
in particular, it does not need the result_type,
first_argument_type and second_argument_type standard typedefs.
Using bind with functions and function pointers
Given these definitions:
int f(int a, int b)
{
return a + b;
}
int g(int a, int b, int c)
{
return a + b + c;
}
bind(f, 1, 2) will produce a "nullary" function object that
takes no arguments and returns f(1, 2). Similarly,
bind(g, 1, 2, 3)() is equivalent to g(1, 2, 3).
It is possible to selectively bind only some of the arguments.
bind(f, _1, 5)(x) is equivalent to f(x, 5); here
_1 is a placeholder argument that means "substitute with the first
input argument."
For comparison, here is the same operation expressed with the standard
library primitives:
std::bind1st(std::ptr_fun(f), 5)(x);
bind covers the functionality of std::bind2nd as well:
std::bind2nd(std::ptr_fun(f), 5)(x); // f(5, x)
bind(f, 5, _1)(x); // f(5, x)
bind can handle functions with more than two arguments, and its
argument substitution mechanism is more general:
bind(f, _2, _1)(x, y); // f(y, x)
bind(g, _1, 9, _1)(x); // g(x, 9, x)
bind(g, _3, _3, _3)(x, y, z); // g(z, z, z)
bind(g, _1, _1, _1)(x, y, z); // g(x, x, x)
The arguments that bind takes are copied and held internally by
the returned function object. For example, in the following code:
int i = 5;
bind(f, i, _1);
a copy of the value of i is stored into the function object.
boost::ref and boost::cref
can be used to make the function object store a reference to an object,
rather than a copy:
int i = 5;
bind(f, ref(i), _1);
Using bind with function objects
Any function object can be passed as a first argument to bind, but the
syntax is a bit different. The return type of the generated function object's
operator() has to be specified explicitly (without a typeof
operator the return type cannot be inferred in the general case):
struct F
{
int operator()(int a, int b) { return a - b; }
bool operator()(long a, long b) { return a == b; }
};
F f;
int x = 104;
bind<int>(f, _1, _1)(x); // f(x, x), i.e. zero
[Note: when, hopefully,
function template default arguments become part of C++,
bind will no longer require the explicit specification of the return type
when the function object defines result_type.]
Using bind with member function pointers
Pointers to member functions are not function objects, because they do not
support operator(). For convenience, bind accepts member function
pointers as its first argument, and the behavior is as if
boost::mem_fn has been used to convert the member
function pointer into a function object. In other words, the expression
bind(&X::f, args)
is equivalent to
bind<R>(mem_fn(&X::f), args)
where R is the return type of X::f.
[Note: mem_fn creates
function objects that are able to accept a pointer, a reference, or a smart
pointer to an object as its first argument; for additional information, see
the mem_fn documentation.]
Example:
struct X
{
bool f(int a);
};
X x;
shared_ptr<X> p(new X);
int i = 5;
bind(&X::f, ref(x), _1)(i); // x.f(i)
bind(&X::f, &x, _1)(i); // (&x)->f(i)
bind(&X::f, x, _1)(i); // (internal copy of x).f(i)
bind(&X::f, p, _1)(i); // (internal copy of p)->f(i)
The last two examples are interesting in that they produce "self-contained"
function objects. bind(&X::f, x, _1) stores a copy of x.
bind(&X::f, p, _1) stores a copy of p, and since p
is a
boost::shared_ptr,
the function object retains a reference to its instance of X and will
remain valid even when p goes out of scope or is reset().
Using nested binds for function composition
Some of the arguments passed to bind may be nested bind expressions
themselves:
bind(f, bind(g, _1))(x); // f(g(x))
The nested subexpressions are evaluated when the function object is called. This
feature of bind can be used to perform function composition.
See bind_as_compose.cpp for an example that
demonstrates how to use bind to achieve similar functionality to
Boost.Compose.
Note that the first argument - the bound function object - is an
exception to the nesting rules. A nested bind expression passed
to bind as a first argument is not treated differently from
any other function object:
int x = 4;
template<class F> void test(F f)
{
bind(f, 5)(x);
}
int g(int, int);
int main()
{
test(bind(g, _1, 8)); // g(5, 8) and not g(x, 8)(5)
}
Example: using bind with standard algorithms
class image;
class animation
{
public:
void advance(int ms);
bool inactive() const;
void render(image & target) const;
};
std::vector<animation> anims;
template<class C, class P> void erase_if(C & c, P pred)
{
c.erase(std::remove_if(c.begin(), c.end(), pred), c.end());
}
void update(int ms)
{
std::for_each(anims.begin(), anims.end(), boost::bind(&animation::advance, _1, ms));
erase_if(anims, boost::mem_fn(&animation::inactive));
}
void render(image & target)
{
std::for_each(anims.begin(), anims.end(), boost::bind(&animation::render, _1, boost::ref(target)));
}
Example: using bind with Boost.Function
class button
{
public:
boost::function<void> onClick;
};
class player
{
public:
void play();
void stop();
};
button playButton, stopButton;
player thePlayer;
void connect()
{
playButton.onClick = boost::bind(&player::play, &thePlayer);
stopButton.onClick = boost::bind(&player::stop, &thePlayer);
}
Limitations
The function objects generated by bind take their arguments by
reference and cannot, therefore, accept non-const temporaries or literal
constants. This is an inherent limitation of the C++ language, known as the
"forwarding function problem."
The library uses signatures of the form
template<class T> void f(T & t);
to accept arguments of arbitrary types and pass them on unmodified. As noted,
this does not work with non-const r-values.
An oft-proposed "solution" to this problem is to add an overload:
template<class T> void f(T & t);
template<class T> void f(T const & t);
Unfortunately, this (a) requires providing 512 overloads for nine arguments
and (b) does not actually work for const arguments, both l- and r-values,
since the two templates produce the exact same signature and cannot be
partially ordered.
[Note: this is a dark corner of the language, and the
corresponding issue has not been resolved yet.]
Interface
Synopsis
namespace boost
{
// no arguments
template<class R, class F> implementation-defined-1 bind(F f);
template<class R> implementation-defined-2 bind(R (*f) ());
// one argument
template<class R, class F, class A1> implementation-defined-3 bind(F f, A1 a1);
template<class R, class B1, class A1> implementation-defined-4 bind(R (*f) (B1), A1 a1);
template<class R, class T, class A1> implementation-defined-5 bind(R (T::*f) (), A1 a1);
template<class R, class T, class A1> implementation-defined-6 bind(R (T::*f) () const, A1 a1);
// two arguments
template<class R, class F, class A1, class A2> implementation-defined-7 bind(F f, A1 a1, A2 a2);
template<class R, class B1, class B2, class A1, class A2> implementation-defined-8 bind(R (*f) (B1, B2), A1 a1, A2 a2);
template<class R, class T, class B1, class A1, class A2> implementation-defined-9 bind(R (T::*f) (B1), A1 a1, A2 a2);
template<class R, class T, class B1, class A1, class A2> implementation-defined-10 bind(R (T::*f) (B1) const, A1 a1, A2 a2);
// implementation defined number of additional overloads for more arguments
}
namespace
{
implementation-defined-placeholder-type-1 _1;
implementation-defined-placeholder-type-2 _2;
implementation-defined-placeholder-type-3 _3;
// implementation defined number of additional placeholder definitions
}
Common requirements
All implementation-defined-N types returned by bind are
CopyConstructible. implementation-defined-N::result_type is defined as
the return type of implementation-defined-N::operator().
All implementation-defined-placeholder-N types are
CopyConstructible. Their copy constructors do not throw exceptions.
Common definitions
The function µ(x, v1, v2, ..., vm), where m is a nonnegative integer, is defined as:
- x.get(), when x is of type
boost::reference_wrapper<T> for some type T;
- vk, when x is (a copy of) the placeholder _k for some positive integer k;
- x(v1, v2, ..., vm)
when x is (a copy of) a function object returned by bind;
- x otherwise.
bind
Returns: a function object λ such that the expression
λ(v1, v2, ..., vm) is equivalent to f(),
implicitly converted to R.
Throws: Nothing unless the copy constructor of F throws an exception.
Returns: a function object λ such that the expression
λ(v1, v2, ..., vm) is equivalent to f().
Throws: Nothing.
Returns: a function object λ such that the expression
λ(v1, v2, ..., vm) is equivalent to
f(µ(a1, v1, v2, ..., vm)),
implicitly converted to R.
Throws: Nothing unless the copy constructors of F and A1 throw an exception.
Returns: a function object λ such that the expression
λ(v1, v2, ..., vm) is equivalent to
f(µ(a1, v1, v2, ..., vm)).
Throws: Nothing unless the copy constructor of A1 throws an exception.
Effects: equivalent to bind<R>(boost::mem_fn(f), a1);
Effects: equivalent to bind<R>(boost::mem_fn(f), a1);
Returns: a function object λ such that the expression
λ(v1, v2, ..., vm) is equivalent to
f(µ(a1, v1, v2, ..., vm), µ(a2, v1, v2, ..., vm)),
implicitly converted to R.
Throws: Nothing unless the copy constructors of F, A1 and A2 throw an exception.
Returns: a function object λ such that the expression
λ(v1, v2, ..., vm) is equivalent to
f(µ(a1, v1, v2, ..., vm), µ(a2, v1, v2, ..., vm)).
Throws: Nothing unless the copy constructors of A1 and A2 throw an exception.
Effects: equivalent to bind<R>(boost::mem_fn(f), a1, a2);
Effects: equivalent to bind<R>(boost::mem_fn(f), a1, a2);
Implementation
This implementation supports function objects with up to nine arguments.
This is an implementation detail, not an inherent limitation of the
design.
Void returns
The following C++ code:
void f();
void g()
{
return f();
}
is legal; in fact it was deliberately made legal in order to support
forwarding functions that return void.
Unfortunately, some compilers have not caught up with the C++ Standard yet
and do not allow void returns. This implementation of bind will not
work for function pointers, member function pointers or function objects that
return void if the compiler does not support the feature. A possible
workaround is to change the return type of the function object in question
from void to int and return a dummy value of 0.
MSVC specific problems and workarounds
Microsoft Visual C++ (up to version 7.0) does not fully support the
bind<R>(...)
syntax required by the library when arbitrary function objects are bound.
The first problem is that when boost::bind is brought into scope
with an using declaration:
using boost::bind;
the syntax above does not work. Workaround: either use the qualified name,
boost::bind, or use an using directive instead:
using namespace boost;
The second problem is that some libraries contain nested class templates
named bind (ironically, such code is often an MSVC specific
workaround.) Due to some quirks with the parser, such a class template
breaks the bind<R>(...) syntax, even when the name bind
is fully qualified. You may try to patch the library in question or contact
its author/maintainer. The other option is to define the macro
BOOST_BIND to something other than bind (before the inclusion of
<boost/bind.hpp>) and use this identifier throughout your code
wherever you'd normally use bind.
[Note: BOOST_BIND is not a general renaming mechanism. It is not part of the
interface, and is not guaranteed to work on other compilers, or persist between
library versions. In short, don't use it unless you absolutely have to.]
Visitor support
[Note: this is an experimental feature. It may evolve over time
when other libraries start to exploit it; or it may be removed altogether if
no other library needs it. It is not part of the interface.]
For better integration with other libraries, the function objects returned by
bind define a member function
template<class Visitor> void accept(Visitor & v);
that applies v, as a function object, to its internal state. Using
accept is implementation-specific and not intended for end users.
See bind_test_4.cpp for an example.
Acknowledgements
Earlier efforts that have influenced the library design:
Doug Gregor suggested that a visitor mechanism would allow bind to
interoperate with a signal/slot library.
John Maddock fixed a MSVC-specific conflict between bind and the
type traits library.
Numerous improvements were suggested during the formal review period by
Ross Smith, Richard Crossley, Jens Maurer, Ed Brey, and others. Review manager
was Darin Adler.
The precise semantics of bind were refined in discussions with Jaakko Järvi.
Copyright © 2001 by Peter Dimov and Multi Media
Ltd. Permission to copy, use, modify, sell and distribute this document is
granted provided this copyright notice appears in all copies. This document
is provided "as is" without express or implied warranty, and with
no claim as to its suitability for any purpose.
|