Callable Objects: Using std::function and std::bind

Share the Article

There are various ways to implement callback methods. Basically, the traditional way is to use function pointers or function references. However, C++11 introduces a notion of callable objects. These objects are generic function pointers. They include, not only function pointers and function references from C but also, lambdas, and functors.

Function Pointer

To start with, the following example illustrates the use of callback method using a simple function pointer. Here, “fp” is the type which denotes a function pointer. Such function must accept one integer argument and returns void type.

#include <iostream> //main header using namespace std; //namespace typedef void(*fp)(int); //fp is type pointer to function void funda(int xvar) { cout << "Function called with :" << xvar << endl; } void testCallBack(fp ptr, int arg) { ptr(arg); } int main() { testCallBack(funda, 4); return 0; }

Output

Function called with :4

Problem with function pointers

Function pointers are very rigid and they require exact types of parameter and return value. These function pointer can neither accept a non-static class member nor a functor.

The following program uses same function pointer but this time, the program passes a functor. Clearly, the compiler shall generate an error in this case.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace typedef void(*fp)(int); void funda(int xvar) { cout << "Function called with :" << xvar << endl; } class MainFunda { public: void operator()(int xvar) { cout << "Functor called with :" << xvar << endl; } }; void testCallBack(fp ptr, int arg) { ptr(arg); } int main() { testCallBack(funda, 4); // funda is function pointer MainFunda avar; testCallBack(avar, 5); // avar is functor return 0; }

The error message is:

In C++ typedef has problems when function objects passed

Callable Objects

These are like, generalized function pointers and they consist of anything which can be called like, a function. For example, lambda, functors, non-static member functions.

In C++11, a new template class std::function represents all such callable objects. This class is a type-safe wrapper for all the entities which we use just like function calls.

The following example, now defines “fp” as any callable object type which needs one integer parameter and returns void. We will see that this type now accept, not only function pointer but also lambda and functor.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace //typedef void(fp)(int); using fp = std::function<void(int)>; void funda(int xvar) { cout << "Function called with :" << xvar << endl; } class MainFunda { public: void operator()(int xvar) { cout << "Functor called with :" << xvar << endl; } }; void testCallBack(fp ptr, int arg) //Accepts callable objects { ptr(arg); } int main() { testCallBack(funda, 4); //funda is Function pointer MainFunda avar; testCallBack(avar, 5); //avar is functor auto lc = [](int xvar) { cout << "Lambda called with :" << xvar << endl; }; testCallBack(lc, 6); //lc is lambda return 0; }

The output is:

Function called with :4 Functor called with :5 Lambda called with :6

Comparing callable objects with nullptr

The function callable objects support comparison with nullptr. This is because, if nothing has initialised a callable object, then it essentially become like a null object.

The following example, creates 2 function objects f1 & f2. And only one of them is initialized (with lambda). The other one will become equivalent to nullptr.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespace //typedef void(fp)(int); using fp = std::function<void(int)>; void checkCallback(fp f) { if(f==nullptr) cout << "No callback assigned" << endl; else cout << "callback assigned" << endl; } int main() { auto lc = [](int xvar) { cout << "Lambda called with :" << xvar << endl; }; fp fvar1; //This callable object is empty fp fvar2 = lc; //This callable object is not empty checkCallback(fvar1); checkCallback(fvar2); return 0; }
No callback assigned callback assigned

Comparison of function callable objects not allowed

The std::function class do not allow comparison of two objects. Internally, the operator==( ) and operator!=( ) are deleted functions.

void compareCallbacks(fp fvar1, fp fvar2) //fp is callable { if(fvar1==fvar2) //error { } if(fvar1!=fvar2) //error { } }

Understanding the use of std::bind for callable objects

The function std::bind( ) also creates a callable object. Additionally, it maps the parameters of function objects with predefined values or with any of actual arguments passed in the call.

Bind the parameters with fixed values

The following function first creates a callable std::function object “fp” using a function pointer. And in second step, it uses bind to map parameter of this callable object with fixed values. Now, whenever, the callable object is called, it only uses mapped values.

#include <iostream> //main header #include <functional> //for function ptr using namespace std; //namespaces using namespace std::placeholders; void funda(int avar, int bvar, int cvar, int dvar) { cout << "\nfirst arg=" << avar << endl; cout << "second arg=" << bvar << endl; cout << "third arg=" << cvar << endl; cout << "fourth arg=" << dvar << endl; } int main() { std::function<void(int, int, int, int)> fp = funda; fp(1,2,3,4); //Before bind fp = std::bind(fun, 10, 20, 30, 40); fp(1,2,3,4); //After bind return 0; }

The output is:

first arg=1 => Before bind second arg=2 third arg=3 fourth arg=4 first arg=10 => After bind second arg=20 third arg=30 fourth arg=40

Using placeholders to map parameters with actual arguments

The nth argument passed to in actual call is denoted with “_n”. These actual arguments are denoted as placeholders. The placeholders can map any actual argument with any parameter of callable object.

For instance, the following example, there are 2 bind calls,

The previous call, maps second (_2) and fourth (_4) actual argument with corresponding parameters positions in the callable objects. And after that, the second bind function reverses that connection between second and forth mapping.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespaces using namespace std::placeholders; void funda(int avar, int bvar, int cvar, int dvar) { cout << "\nfirst arg=" << avar << endl; cout << "second arg=" << bvar << endl; cout << "third arg=" << cvar << endl; cout << "fourth arg=" << dvar << endl; } int main() { std::function<void(int, int, int, int)> fp = funda; fp(1,2,3,4); fp = std::bind(funda, 10, _2, 30, _4);//second to second //forth to forth fp(1,2,3,4); fp = std::bind(funda, 10, _4, 30, _2);//forth to second, //second to forth fp(1,2,3,4); return 0; }

The output is:

first arg=1 second arg=2 third arg=3 fourth arg=4 first arg=10 second arg=2 third arg=30 fourth arg=4 first arg=10 second arg=4 third arg=30 fourth arg=2

Application of std::bind( ) : Implementation of an Adaptor

The bind function can modify the mapping between actual arguments and the parameters of callable objects. Therefore, such feature helps in implementing an adaptor between 2 interfaces.

The following example, internally maps the inputs of a 2D object to corresponding inputs of a 3D object. Specifically, for completion, it maps the third parameter of 3D to a constant value. Finally, the 3D object’s draw becomes a callback for 2D object’s draw.

#include <iostream> //main header #include <functional>//for function ptr using namespace std; //namespaces using namespace std::placeholders; using cb2DType = std::function<void(int, int)>; class mainfunda2D { public: void setCallback(const cb2DType& cb) { drawCallback = cb; } void draw(int x, int y) { drawCallback(x,y); } private: cb2DType drawCallback; }; class mainfunda3D { public: void draw (int x, int y, int z) { cout << "Draw on (" << x << ", " << y << ", " << z << ")" << endl; } }; int main() { mainfunda2D _2d; mainfunda3D _3d; _2d.setCallback(std::bind(&mainfunda3D::draw, _3d, _1, _2, 50) ); _2d.draw(4,5); return 0; }

The output is:

Draw on (4, 5, 50)

Main Funda: C++11 callable objects are like, generic function pointers, they are are called like functions.

Related Topics:

 What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism
emplace_back vs push_back


Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *