wake-up-neo.net

Verwendung von generischen std :: function-Objekten mit Mitgliedsfunktionen in einer Klasse

Für eine Klasse möchte ich einige Funktionszeiger auf Mitgliedsfunktionen derselben Klasse in einem map speichern, in dem std::function - Objekte gespeichert sind. Aber ich scheitere gleich am Anfang mit diesem Code:

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

Ich erhalte error C2064: term does not evaluate to a function taking 0 arguments In xxcallobj, kombiniert mit einigen seltsamen Vorlageninstanziierungsfehlern. Derzeit arbeite ich unter Windows 8 mit Visual Studio 2010/2011 und unter Win 7 mit VS10 schlägt dies ebenfalls fehl. Der Fehler muss auf einigen seltsamen C++ - Regeln basieren, denen ich nicht folge.

EDIT: Ich mache NICHT Boost verwenden. Dies ist C++ 11, das in den MS-Compiler integriert ist.

138

Eine nicht statische Elementfunktion muss mit einem Objekt aufgerufen werden. Das heißt, es wird immer implizit "dieser" Zeiger als Argument übergeben.

Da Ihre std::function - Signatur angibt, dass Ihre Funktion keine Argumente akzeptiert (<void(void)>), müssen Sie bind das erste (und das einzige) Argument.

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

Wenn Sie eine Funktion mit Parametern binden möchten, müssen Sie Platzhalter angeben:

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

Oder wenn Ihr Compiler C++ 11 Lambdas unterstützt:

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

(Ich habe keinen C++ 11-fähigen Compiler zur Hand im Moment, daher kann ich diesen nicht überprüfen.)

251
Alex B

Entweder du brauchst

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

damit Sie es für jede Instanz aufrufen können oder eine bestimmte Instanz binden müssen, z. B. this

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

Wenn Sie eine Member-Funktion ohne die Klasseninstanz speichern müssen, können Sie folgendermaßen vorgehen:

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

Wie würde der Speichertyp ohne auto aussehen? Etwas wie das:

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

Sie können diesen Funktionsspeicher auch an eine Standardfunktionsbindung übergeben

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

Vergangene und zukünftige Notizen: Eine ältere Schnittstelle std :: mem_func existierte, wurde aber inzwischen nicht mehr weiterentwickelt. Es gibt einen Vorschlag, nach C++ 17 Zeiger auf aufrufbare Elementfunktionen zu machen. Das wäre sehr willkommen.

11
Greg

Sie können Funktoren verwenden, wenn Sie eine weniger allgemeine und präzisere Steuerung unter der Haube wünschen. Beispiel mit meiner Win32-API, um eine API-Nachricht von einer Klasse an eine andere Klasse weiterzuleiten.

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

};

Nutzungsprinzipien

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

Viel Glück und vielen Dank für das Teilen von Wissen.

0
user11158633