Skip to content

Using std::bind for fun and profit!

August 6, 2012

Introduction

A few days ago, I was messing with some old code of mine that uses the Boost random number library, looking at upgrading it to use the new random number features of C++11. Using the library requires the use of the boost::bind function, and this too can be replaced with the new C++11 std::bind. Unfortunately, I had not used either bind function in some time and had forgotten exactly how they work, and I had to write some simple code to remind myself. This tutorial, which explains what std::bind does, and why you may want to use, is an adaptation of the code I wrote. In order to follow it, you will need some (but not a great deal) knowledge of C++ and (if you want to compile the code) a C++ compiler that supports the C++11 <functional> facilities – I used GCC 4.6.1.

Source code for the examples is available at https://bitbucket.org/neilb/bind-article – use the "get source" menu if you don’t have Mercurial installed.

Some Very Simple Stuff

We’ll start by writing a very simple program that implements a function which adds two integers passed as parameters, and returns the result of the addition:

#include <iostream> using namespace std; int add( int a, int b ) { return a + b; } int main() { cout << add( 1, 2 ) << "\n"; }

If we compile this with GCC (note the use of the -std option to say we want the compiler to treat the code as C++11, or "c++0x" as the version of GCC I use insists on calling it):

g++ -std=c++0x prog1.cpp

and run the resulting executable, we get the exciting output:

3

Now, suppose (for some mad reason) we want to write a function that always adds 1 and 2 together, and always does so using the add function. We could write something like this:

int add12() {
    return add( 1, 2 );
}

and call it:

cout << add12() << "\n";

This works, but means that if a bit later we find we want to add 3 and 4, we’d have to write another function, add34(). The number of  functions we’d need to write could soon get out of hand. It would be nice if we could create the functions as and when we needed them without having to write separate function definitions for each one. In effect, this is what the std::bind function allows us to do.

Enter std::bind

Using the std::bind function, which is defined in the <functional> Standard Library header file, we can create a new function called add12 without having to write the function body:

#include <iostream>
#include <functional>
using namespace std;

int add( int a, int b ) {
    return a + b;
}

int main() {
    auto add12 = bind( add, 1, 2 );
    cout << add12() << "\n";
}

This code, as in the previous examples, prints out the value "3". So how does it do it?

What the bind function does is to create a new object from its parameters. The first parameter is the name of the function we wish to use (in this case the add function), and the remaining parameters are the values that we wish to bind to the formal parameters of the named function. In this case we wish to bind the value 1 to the formal parameter a, and the value 2 to the formal parameter b. This means that when we call the function via the add12 object, it is as if we had said add( 1, 2 ). Note that there is nothing magical about the name add12, we could equally well have written:

auto f = bind( add, 1, 2 );
cout << f() << "\n";

For the moment, don’t worry about what kind of things add12 and f in the above code actually are – just think of them as new functions that bind created for you.

Binding Variables And References

In the code we’ve written so far, we have just bound constant values to the function, but variables and references can also be bound. This code binds the two variables x and y to the function add, and outputs (yet again) the value "3":

int main() {
    int x = 1, y = 2;
    auto addxy = bind( add, x, y );
    cout << addxy() << "\n";
}

For variables, the value they represent is evaluated at the point binding takes place. If you change the variables after binding, the original values are still used, so this prints out "3" and "3", not "3" and "7";

int main() {
    int x = 1, y = 2;
    auto addxy = bind( add, x, y );
    cout << addxy() << "\n";
    x = 3;
    y = 4;
    cout << addxy() << "\n";
}

To get the binding to use the current values of x and y, we would need to bind references to those variables. A special helper function is used to do this:

int main() {
    int x = 1, y = 2;
    auto addxy = bind( add, cref( x ), cref( y ) );
    cout << addxy() << "\n";
    x = 3;
    y = 4;
    cout << addxy() << "\n";
}

This now prints out "3" and "7" – the std::cref function performs binding by reference rather than by value, so that every time addxy is called, the current values of x and y are used..

Placeholders

Suppose we need a function add1, that works like this:

add1( 42 ); // returns 43

add1( x ); // returns x + 1

We would need to perform a binding which only binds _one_ of the add function’s formal parameters, with the other one being supplied when we call the function via the object returned by bind.  This is how to do it:

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

int add( int a, int b ) {
    return a + b;
}

int main() {
    auto add1 = bind( add, 1, _1 );
    cout << add1( 42 ) << "\n";
}

The special value _1 is a placeholder, defined in the std::placeholders namespace. Placeholders stand for the parameter values that will be used when the bound function is actually called; _1 is the first parameter, _2 the second and so on. The actual number of placeholders available is implementation defined.

So, in the above code the placeholder _1 will be replaced by the value 42 (the first parameter of add1) when the function is actually called, and the result will be that "43" is printed.

What does std::bind really return?

Earlier on, I said not to worry about what kind of thing calling std::bind actually produces. The reason for this is fairly simple, the C++ Standard does not specify exactly what type of thing this should be! The Standard does lay down some requirements on what the type must be able to do, but does not say what it must be called, or really how it must work – these are details left to the implementation.

For example, for this code:

auto add12 = bind( add, 1, 2 );

the GCC compiler returns an object of the following type:

std::_Bind_helper<int (&)(int, int), int, int>::type

Phew! We would not want to have to enter that every time you used std::bind (and things get much worse when placeholders are used), which is why in all the code presented so far, we have used the new auto feature of C++11 which deduces the required type for us.

However, occasionally we will need to create an object of known type, and in those cases we can take refuge in the fact that whatever type std::bind actually returns, it must be storable in a std::function object. Thus we can write code like this:

int main() {
    std::function <int(void)> addup;
    int z;
    cin >> z;
    if ( z % 2 ) 
        addup = bind( add, 1, 2 );
    else 
        addup = bind( add, 3, 4 );
    cout << addup() << "\n";
}

If you run the above code and enter an odd number, it displays "3", if you enter an even number "7". We cannot  write directly equivalent the code using auto because of the scope issues.

But What’s It All For?

You may at this point be wondering what the point of all this is. Well, in general, the purpose of std::bind is to adapt functions to situations where we want their functionality, but where they have the wrong numbers, types or positions of parameters. Here’s an example; suppose we have a function called apply, which works on std::strings:

template <typename FUNC>
string apply( const string & s, FUNC f ) {
    string result;
    for ( size_t i = 0; i < s.size(); i++ ) {
        result += f( s[i] );
    }
    return result;
}

This function takes a string and the name of the a function as parameters, and applies the function to each single character in the string, constructing a new string from the function’s return values. 

This will work well for functions that take a single char as their parameter. For example, this function returns the next character in the ASCII character set (assuming ASCII encoding) from the parameter value:

char nextchar( char c ) {
	return c + 1;
}

so if we write some code like this:

string x = "foobar";
cout << apply( x, nextchar ) << "\n";

the result is that;

gppcbs

is displayed.

However, suppose we have a function which takes more than one parameter, such as this one allows us to specify a list of characters which we wish to change, so that only characters on the list will shifted to the next character:

char nextif( char c, const string & chars ) {
    if ( chars.find( c ) != string::npos ) {
        return c + 1;
    }
    else {
        return c;
    }
}

In other words, calling:

nextf( 'f', "aeiou" );

would return the value ‘f’ unchanged, as ‘f’ is not in the list of vowels.

How can we call this function with apply? We cannot say:

apply( x, nextif );

or:

apply( x, nextif, "aeiou" );

as both will result in type errors. This calls for std::bind! This does the job:

cout << apply( x, bind( nextif, _1, "aeiou"  ) ) << "\n";

Notice that we did not need to actually name the new function that std::bind creates, we can simply pass the new (nameless) function directly to apply.

Now, you may think "Well, I simply won’t write functions like ‘apply’!" – possibly not, but the <algorithm> section of the C++ Standard Library is full of things that look very like the apply function presented here, and if you want to use them, you will probably have to also use std::bind.

Summary

This has been quite a long article, and a lot has been covered (though by no means all – I haven’t address binding member functions). The main thing to take away from this is that std::bind provides an easy means of adapting functions (for which you may well not have the source code) to situations where they would not otherwise be callable without writing (possibly many, possibly otherwise unnecessary)  wrapper functions.

From → c++, tutorial

Leave a Comment

Leave a comment