Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the template parameter of std::function work? (implementation)

In Bjarne Stroustrup's home page (C++11 FAQ):

struct X { int foo(int); };  std::function<int(X*, int)> f; f = &X::foo; //pointer to member  X x; int v = f(&x, 5); //call X::foo() for x with 5 

How does it work? How does std::function call a foo member function?

The template parameter is int(X*, int), is &X::foo converted from the member function pointer to a non-member function pointer?!

(int(*)(X*, int))&X::foo //casting (int(X::*)(int) to (int(*)(X*, int)) 

To clarify: I know that we don't need to cast any pointer to use std::function, but I don't know how the internals of std::function handle this incompatibility between a member function pointer and a non-member function pointer. I don't know how the standard allows us to implement something like std::function!

like image 730
Sadeq Avatar asked Aug 20 '10 20:08

Sadeq


People also ask

Can a template parameter be a function?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

How do you call a function in a template?

Defining a Function TemplateA function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.

Why do we use template template parameter?

Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.

Which is a correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.


2 Answers

After getting help from other answers and comments, and reading GCC source code and C++11 standard, I found that it is possible to parse a function type (its return type and its argument types) by using partial template specialization and function overloading.

The following is a simple (and incomplete) example to implement something like std::function:

template<class T> class Function { };  // Parse the function type template<class Res, class Obj, class... ArgTypes> class Function<Res (Obj*, ArgTypes...)> {     union Pointers {         Res (*func)(Obj*, ArgTypes...);         Res (Obj::*mem_func)(ArgTypes...);     };      typedef Res Callback(Pointers&, Obj&, ArgTypes...);      Pointers ptrs;     Callback* callback;      static Res call_func(Pointers& ptrs, Obj& obj, ArgTypes... args) {         return (*ptrs.func)(&obj, args...);     }      static Res call_mem_func(Pointers& ptrs, Obj& obj, ArgTypes... args) {         return (obj.*(ptrs.mem_func))(args...);     }    public:      Function() : callback(0) { }      // Parse the function type     Function(Res (*func)(Obj*, ArgTypes...)) {         ptrs.func = func;         callback = &call_func;     }      // Parse the function type     Function(Res (Obj::*mem_func)(ArgTypes...)) {         ptrs.mem_func = mem_func;         callback = &call_mem_func;     }      Function(const Function& function) {         ptrs = function.ptrs;         callback = function.callback;     }      Function& operator=(const Function& function) {         ptrs = function.ptrs;         callback = function.callback;         return *this;     }      Res operator()(Obj& obj, ArgTypes... args) {         if(callback == 0) throw 0; // throw an exception         return (*callback)(ptrs, obj, args...);     } }; 

Usage:

#include <iostream>  struct Funny {     void print(int i) {         std::cout << "void (Funny::*)(int): " << i << std::endl;     } };  void print(Funny* funny, int i) {     std::cout << "void (*)(Funny*, int): " << i << std::endl; }  int main(int argc, char** argv) {     Funny funny;     Function<void(Funny*, int)> wmw;      wmw = &Funny::print; // void (Funny::*)(int)     wmw(funny, 10); // void (Funny::*)(int)      wmw = &print; // void (*)(Funny*, int)     wmw(funny, 8); // void (*)(Funny*, int)      return 0; } 
like image 195
Sadeq Avatar answered Oct 11 '22 16:10

Sadeq


How it does it (I believe) is left undefined (but I don't have a copy of the standard here).

But given all the different possibilities that need to be covered I have the feeling that deciphering the exact definition of how it works would be really hard: So I am not going to try.

But I think you would like to know how functors work and they are relatively simple. So here is a quick example.

Functors:

These are objects that act like functions.
They are very useful in template code as they often allow you to use objects or functions interchangeably. The great thing about functors though is that they can hold state (a sort of poor man's closure).

struct X {      int operator()(int x) { return doStuff(x+1);}      int doStuff(int x)    { return x+1;} };  X   x;  // You can now use x like a function int  a = x(5); 

You can use the fact that functor hold state to hold things like parameters or the objects or the pointer to member methods (or any combination thereof).

struct Y // Hold a member function pointer {     int (X::*member)(int x);     int operator(X* obj, int param) { return (obj->*member)(param);} }; X  x; Y  y; y.member = &X::doStuff; int a = y(&x,5); 

Or even go further and bind parameters. So now all you need to provide is one of the parameters.

struct Z {     int (X::*member)(int x);     int  param;     Z(int (X::*m)(int), int p) : member(m), param(p) {}      int operator()(X* obj)  { return (obj->*member)(param);}     int operator()(X& obj)  { return (obj.*member)(param);} };  Z z(&X::doStuff,5);  X x; int a = z(x); 
like image 35
Martin York Avatar answered Oct 11 '22 14:10

Martin York