57

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!

Gyorgy Szekely
  • 2,654
  • 3
  • 23
  • 32
Sadeq
  • 7,795
  • 4
  • 34
  • 45
  • 1
    If you are referring to another web page. Please always provide the link. – Martin York Aug 20 '10 at 20:59
  • 1
    The page is here: http://www2.research.att.com/~bs/C++0xFAQ.html#std-function . Bjarne asserts that this is legal, but doesn't mention the mechanics of how it works (whether its clever templating, or some sort of new built-in conversion). – Tyler McHenry Aug 20 '10 at 21:15

5 Answers5

35

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;
}
Sadeq
  • 7,795
  • 4
  • 34
  • 45
4

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);
Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thanks for the info, although i know what a functor (function object) is, and i tried to know : "How the std::function holds/calls a member/non-member function pointer?" and i couldn't. – Sadeq Aug 20 '10 at 21:41
  • It probably uses a whole host of template specializations. But holding a function pointer is no different to holding a method pointer it is just the types that are different. – Martin York Aug 20 '10 at 21:45
3

To answer the question in the title. The parameter that std::function uses is a nice trick to pass many type parameters as a single template parameter. Those arguments being the argument types and the return type of a function.

It turns out that std::function tries to type-erase a general functor but that is just coincidence.

As a matter of fact, once upon a time there were compilers that wouldn't accept such tricks and the boost::function precursor had a portable syntax by which all the parameters could be passed separately:

Preferred syntax

boost::function<void(int*, int, int&, float&)> sum_avg;

Portable syntax

boost::function4<void, int*, int, int&, float&> sum_avg;

https://www.boost.org/doc/libs/1_68_0/doc/html/function/tutorial.html#id-1.3.16.5.4

So that's how the template parameters of std::function work, at the end it is just a trick to make a lot of parameters look like a function call. Function pointers to that type of function are not necessarily involved in the class.

alfC
  • 14,261
  • 4
  • 67
  • 118
2

They're not function pointers. That's what std::function exists for. It wraps whatever callable types you give it. You should check out boost::bind- it's often used to make member function pointers callable as (this, args).

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 4
    His question is still valid, though. The `std::function` instantiation is parametrized with `int (X*, int)`, which is not the same type as the type of `&X::foo`, which is assigned to it. Although it's clear how you could invoke the latter given the arguments to the former, from the compiler's perspective, these types are not inherently related, so it's not obvious how this is allowed. – Tyler McHenry Aug 20 '10 at 21:13
2

g++ seems to have an union which may keep either function pointer, member pointer or void pointer which probably points to a functor. Add overloads which appropriately flag which union member is valid and heavy casting to a soup and then it works...

Tomek
  • 4,554
  • 1
  • 19
  • 19
  • Thanks, i believe that casting _member function pointer_ to _non-member function pointer_ is left undefined behavior! and by using an union, no cast is needed. i should test it. but how the members of this union is detectable by only one template parameter (we just specify one function type for std::function)? – Sadeq Aug 20 '10 at 21:54
  • "void pointer which probably pointer which probably points to a functor" then it would also need to somehow memory-manage this functor? – user102008 Sep 27 '11 at 20:48