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

In the Bjarne Stroustrup homepage ( 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 it works? How does std :: function call a member function of foo?

The template parameter int(X*, int) , is &X::foo converted from a 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 do not need to indicate any pointer to the use of the std :: function, but I do not know how the internal functions of std :: function handle this incompatibility between a pointer to a member function and a -member function pointer. I do not know how the standard allows us to implement something like std :: function!

+53
c ++ c ++ 11 function-object templates function-pointers
Aug 20 '10 at 20:51
source share
4 answers

Thanks for all the answers.

I found a good example from section 14.8.2.5-21 of the standard:

 template<class> struct X { }; template<class R, class ... ArgTypes> struct X<R(int, ArgTypes ...)> { }; template<class ... Types> struct Y { }; template<class T, class ... Types> struct Y<T, Types& ...> { }; template<class ... Types> int f(void (*)(Types ...)); void g(int, float); // uses primary template X<int> x1; // uses partial specialization; ArgTypes contains float, double X<int(int, float, double)> x2; // uses primary template X<int(float, int)> x3; // use primary template; Types is empty Y<> y1; // uses partial specialization; T is int&, Types contains float, double Y<int&, float&, double&> y2; // uses primary template; Types contains int, float, double Y<int, float, double> y3; // OK; Types contains int, float int fv = f(g); 

It says that with the help of template specialization, we can analyze the parameters of a function type template (amazing)! The following is a dirty / simple example of how std :: function works:

 template<class T> struct Function { }; template<class T, class Obj, class... Args> struct Function<T(Obj*, Args...)> // Parsing the function type { enum FuncType { FuncTypeFunc, FuncTypeMemFunc }; union FuncPtr { T(*func)(Obj*, Args...); T(Obj::*mem_func)(Args...); }; FuncType m_flag; FuncPtr m_func_ptr; Function(T(*func)(Obj*, Args...)) // void(*)(Funny*, int, int) { m_flag = FuncTypeFunc; m_func_ptr.func = func; } Function(T(Obj::*mem_func)(Args...)) // void(Funny::*)(int, int) { m_flag = FuncTypeMemFunc; m_func_ptr.mem_func = mem_func; } void play(Obj* obj, Args... args) { switch(m_flag) { case FuncTypeFunc: (*m_func_ptr.func)(obj, args...); break; case FuncTypeMemFunc: (obj->*m_func_ptr.mem_func)(args...); break; } } }; 

Using:

 #include <iostream> struct Funny { void done(int i, int j) { std::cout << "Member Function: " << i << ", " << j << std::endl; } }; void done(Funny* funny, int i, int j) { std::cout << "Function: " << i << ", " << j << std::endl; } int main(int argc, char** argv) { Funny funny; Function<void(Funny*, int, int)> f = &Funny::done; // void(Funny::*)(int, int) Function<void(Funny*, int, int)> g = &done; // void(*)(Funny*, int, int) f.play(&funny, 5, 10); // void(Funny::*)(int, int) g.play(&funny, 5, 10); // void(*)(Funny*, int, int) return 0; } 

Edit: Thanks to Tomek for his good allusion to union s, the above example has changed to hold a pointer to a member / non-member function in one (not two) variable.




Edit: Martin York is right, the switch statement was not a good idea in the above example, so I completely changed the example to work better:

 template<class T> class Function { }; template<class Res, class Obj, class... ArgTypes> class Function<Res (Obj*, ArgTypes...)> // Parsing the function type { union Pointers // An union to hold different kind of pointers { Res (*func)(Obj*, ArgTypes...); // void (*)(Funny*, int) Res (Obj::*mem_func)(ArgTypes...); // void (Funny::*)(int) }; 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...); // void (*)(Funny*, int) } static Res call_mem_func(Pointers& ptrs, Obj& obj, ArgTypes... args) { return (obj.*(ptrs.mem_func))(args...); // void (Funny::*)(int) } public: Function() : callback(0) { } Function(Res (*func)(Obj*, ArgTypes...)) // void (*)(Funny*, int) { ptrs.func = func; callback = &call_func; } Function(Res (Obj::*mem_func)(ArgTypes...)) // void (Funny::*)(int) { 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...); } }; 

Using:

 #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; } 
+31
Aug 21 '10 at 1:27
source share
— -

How this is done (I believe) remains undefined (but I do not have a copy of the standard here).

But given all the different possibilities that you need to embrace, I feel that decrypting an exact definition of how this works would be very difficult: so I'm 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 as functions.
They are very useful in template code because they often allow you to use objects or functions interchangeably. The great thing about functors is that they can hold on to a state (a kind of poor human 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 the functor hold state contains objects, such as parameters or objects, or a pointer to member methods (or any combination of them).

 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 move on and bind the parameters. So now all you need to provide is one of the options.

 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); 
+3
Aug 20 '10 at 21:16
source share

g ++ seems to have a union that can contain either a pointer to a function, or a member pointer, or a void pointer, which probably points to a functor. Add overloads that appropriately mark which union member is valid and throws himself heavily into soup, and then he works ...

+2
Aug 20 '10 at 21:34
source share

They are not function pointers. This is what the std :: function exists for. It wraps up any called types that you give them. You should check boost :: bind - it is often used to point to function pointers (this, args).

+1
Aug 20 '10 at 21:11
source share



All Articles