June 4, 2013
Often when I am unit testing a method which accepts a function pointer, I first write a simple test to verify that the point-to-function is called. The C++11 standard has added
std::bind to easily create function pointers. When I used
std::bind in a recent project, I discovered what I thought was surprising behavior. After a bit more investigation, I found that the behavior is not so surprising at all, and in fact provides some useful flexibility.
To Copy or Not to Copy##
I’ve recently been writing some code which implements a simple map-reduce algorithm using MPI. The constructor for the map-reduce implementation class is defined like this:
The first thing I would expect the
map() method to do is call the provided partitioning method to partition the range of iterators. So I wrote a simple class to allow a unit test to determine if the partitioning method was called.
Then in the unit test, I used
std::bind to bind the to
partition member function like this:
To my surprise, I found that the partitioning method was never called. At least, the
GetPartitioningMethodCalled() method always returned false. It wasn’t until I added a copy constructor to the
PartitioningTracker class that I discovered the problem.
With Great Power Comes Great Responsibility##
This answer on Stack Overflow helped to determine the cause of this behavior. It turns out that
std::bind has a number of overloads. The one I chose made a copy of the tracker object. So although my code in the
map() method was indeed calling the partitioning method, it was calling the method on an instance of
PartitionTracker which existed solely for the purpose of the
std::bind call, not on the instance of
PartitionTracker I had created. When my unit test asserted that
GetPartitioningMethodCalled() returned true, it failed!
Changing the test code to pass the address of the locally created
PartitionTracker instance solved the problem.
The Full Story##
To get a better idea of how the various overloads of
std::bind work, I wrote a simple test program.
This program will output the following:
Bound to copy of tracker Method called _methodCalled value: false Bound to local instance of tracker Method called _methodCalled value: true Bound to refrence to tracker Method called _methodCalled value: true
In all three cases, the method is called as expected, but only the final two cases call the method on the local instance of
Tracker class. You can see the code execute on Ideone here.
In hindsight, this behavior should not have been surprising. C++ uses value semantics to pass arguments by default, so it should have been clear why my unit test was not working as expected initially. In fact, this behavior provides maximum flexibility, allowing the caller of
std::bind to use it as he or she pleases.