Using generic std::function objects with member functions in one class
Asked Answered
A

6

252

For one class I want to store some function pointers to member functions of the same class in one map storing std::function objects. But I fail right at the beginning with this code:

#include <functional>

class Foo {
    public:
        void doSomething() {}
        void bindFunction() {
            // ERROR
            std::function<void(void)> f = &Foo::doSomething;
        }
};

I receive error C2064: term does not evaluate to a function taking 0 arguments in xxcallobj combined with some weird template instantiation errors. Currently I am working on Windows 8 with Visual Studio 2010/2011 and on Win 7 with VS10 it fails too. The error must be based on some weird C++ rules i do not follow

Abductor answered 28/9, 2011 at 11:30 Comment(0)
B
427

A non-static member function must be called with an object. That is, it always implicitly passes "this" pointer as its argument.

Because your std::function signature specifies that your function doesn't take any arguments (<void(void)>), you must bind the first (and the only) argument.

std::function<void(void)> f = std::bind(&Foo::doSomething, this);

If you want to bind a function with parameters, you need to specify placeholders:

using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, std::placeholders::_1, std::placeholders::_2);

Or, if your compiler supports C++11 lambdas:

std::function<void(int,int)> f = [=](int a, int b) {
    this->doSomethingArgs(a, b);
}

(I don't have a C++11 capable compiler at hand right now, so I can't check this one.)

Bezoar answered 28/9, 2011 at 11:32 Comment(6)
As i am not dependant on boost i will use lambda expressions ;) Nevertheless thanks!Abductor
@AlexB : Boost.Bind doesn't use ADL for the placeholders, it puts them in an anonymous namespace.Drysalt
I recommend to avoid global capture [=] and use [this] to make it clearer what is captured (Scott Meyers - Effective Modern C++ Chapter 6. item 31 - Avoid default capture modes)Carl
Just add a little tip: member function pointer can be implicitly cast to std::function, with extra this as it's first parameter, like std::function<void(Foo*, int, int)> = &Foo::doSomethingArgsRuelu
@landerlyoung: Add the name of the function say as "f" above to fix the sample syntax. If you don't need the name you could use mem_fn(&Foo::doSomethingArgs).Obscure
To do this, ``` #include<functional>``` need.Double
S
118

Either you need

std::function<void(Foo*)> f = &Foo::doSomething;

so that you can call it on any instance, or you need to bind a specific instance, for example this

std::function<void(void)> f = std::bind(&Foo::doSomething, this);
Stig answered 28/9, 2011 at 11:32 Comment(4)
Thank you for this great answer :D Exactly what I need, I couldn't find how to specialize a std::function to call a member function on any class instance.Mcclendon
This compiles, but is it standard? Are you guaranteed that the first argument is this?Sarson
@sudorm-rfslash yes, you areStig
Thanks for the reply @ArmenTsirunyan ... where in the standard can I look for this info?Sarson
M
18

If you need to store a member function without the class instance, you can do something like this:

class MyClass
{
public:
    void MemberFunc(int value)
    {
      //do something
    }
};

// Store member function binding
auto callable = std::mem_fn(&MyClass::MemberFunc);

// Call with late supplied 'this'
MyClass myInst;
callable(&myInst, 123);

What would the storage type look like without auto? Something like this:

std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable

You can also pass this function storage to a standard function binding

std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call

Past and future notes: An older interface std::mem_func existed, but has since been deprecated. A proposal exists, post C++17, to make pointer to member functions callable. This would be most welcome.

Munson answered 3/11, 2016 at 4:57 Comment(5)
@Danh std::mem_fn was not removed; a bunch of unnecessary overloads were. On the other hand std::mem_fun was deprecated with C++11 and will be removed with C++17.Undecagon
@Danh That's exactly what I'm talking about ;) The very first "basic" overload is still there: template<class R, class T> unspecified mem_fn(R T::*);, and it won't go away.Undecagon
@Danh Read the DR carefully. 12 out of 13 overloads were removed by the DR. That last one was not (and won't be; neither in C++11 or C++14).Undecagon
Why the down vote? Every other response said you must bind the class instance. If you are creating a binding system for reflection or scripting, you will not want to do that. This alternative method is valid and relevant for some people.Munson
Thanks Danh, I've edited with some comments about related past and future interfaces.Munson
R
15

Unfortunately, C++ does not allow you to directly get a callable object referring to an object and one of its member functions. &Foo::doSomething gives you a "pointer to member function" which refers to the member function but not the associated object.

There are two ways around this, one is to use std::bind to bind the "pointer to member function" to the this pointer. The other is to use a lambda that captures the this pointer and calls the member function.

std::function<void(void)> f = std::bind(&Foo::doSomething, this);
std::function<void(void)> g = [this](){doSomething();};

I would prefer the latter.

With g++ at least binding a member function to this will result in an object three-pointers in size, assigning this to an std::function will result in dynamic memory allocation.

On the other hand, a lambda that captures this is only one pointer in size, assigning it to an std::function will not result in dynamic memory allocation with g++.

While I have not verified this with other compilers, I suspect similar results will be found there.

Radiothorium answered 18/11, 2019 at 15:5 Comment(2)
Both solutions are bad IMO. Bad in terms of it's too much code for something like this(I mean for C++, not you). It should be just: std::function<void(void)> g = doSomething; for a variable, and for a function that takes the std function as an argument: void MyFunction(std::function<void(void)> callback);, when we call it, we should be able to just do MyFunction(doSomething); and be done with it, instead of wrapping it in a lambda capturing this or std::bind, the this should be implied if other object is not provided(same as it is implied in functions already).Claustrophobia
And when we want to provide a different object for the member function, we should be able to just do MyFunction({someOtherObject, doSomething});. Less typing, often less lines, and easier to comprehend than std::bind or a capturing-this lambda.Claustrophobia
S
8

You can avoid std::bind doing this:

std::function<void(void)> f = [this]-> {Foo::doSomething();}
Stinky answered 28/5, 2020 at 10:45 Comment(0)
P
1

You can use functors if you want a less generic and more precise control under the hood. Example with my win32 api to forward api message from a class to another class.

IListener.h

#include <windows.h>
class IListener { 
    public:
    virtual ~IListener() {}
    virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};

Listener.h

#include "IListener.h"
template <typename D> class Listener : public IListener {
    public:
    typedef LRESULT (D::*WMFuncPtr)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

    private:
    D* _instance;
    WMFuncPtr _wmFuncPtr; 

    public:
    virtual ~Listener() {}
    virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) override {
        return (_instance->*_wmFuncPtr)(hWnd, uMsg, wParam, lParam);
    }

    Listener(D* instance, WMFuncPtr wmFuncPtr) {
        _instance = instance;
        _wmFuncPtr = wmFuncPtr;
    }
};

Dispatcher.h

#include <map>
#include "Listener.h"

class Dispatcher {
    private:
        //Storage map for message/pointers
        std::map<UINT /*WM_MESSAGE*/, IListener*> _listeners; 

    public:
        virtual ~Dispatcher() { //clear the map }

        //Return a previously registered callable funtion pointer for uMsg.
        IListener* get(UINT uMsg) {
            typename std::map<UINT, IListener*>::iterator itEvt;
            if((itEvt = _listeners.find(uMsg)) == _listeners.end()) {
                return NULL;
            }
            return itEvt->second;
        }

        //Set a member function to receive message. 
        //Example Button->add<MyClass>(WM_COMMAND, this, &MyClass::myfunc);
        template <typename D> void add(UINT uMsg, D* instance, typename Listener<D>::WMFuncPtr wmFuncPtr) {
            _listeners[uMsg] = new Listener<D>(instance, wmFuncPtr);
        }

};

Usage principles

class Button {
    public:
    Dispatcher _dispatcher;
    //button window forward all received message to a listener
    LRESULT onMessage(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
        //to return a precise message like WM_CREATE, you have just
        //search it in the map.
        return _dispatcher[uMsg](hWnd, uMsg, w, l);
    }
};

class Myclass {
    Button _button;
    //the listener for Button messages
    LRESULT button_listener(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
        return 0;
    }

    //Register the listener for Button messages
    void initialize() {
        //now all message received from button are forwarded to button_listener function 
       _button._dispatcher.add(WM_CREATE, this, &Myclass::button_listener);
    }
};

Good luck and thank to all for sharing knowledge.

Plebeian answered 6/3, 2019 at 8:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.