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

C++TemplatesC++11Function PointersFunction Object

C++ Problem Overview


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!

C++ Solutions


Solution 1 - C++

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;
}

Solution 2 - C++

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);

Solution 3 - C++

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 sum_avg; > > Portable syntax > > boost::function4 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.

Solution 4 - C++

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).

Solution 5 - C++

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...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSadeqView Question on Stackoverflow
Solution 1 - C++SadeqView Answer on Stackoverflow
Solution 2 - C++Martin YorkView Answer on Stackoverflow
Solution 3 - C++alfCView Answer on Stackoverflow
Solution 4 - C++PuppyView Answer on Stackoverflow
Solution 5 - C++TomekView Answer on Stackoverflow