Pour une classe, je souhaite stocker des pointeurs de fonction vers des fonctions membres de la même classe dans un seul objet map
, stockant std::function
. Mais je manque tout de suite avec ce code:
class Foo {
public:
void doSomething() {}
void bindFunction() {
// ERROR
std::function<void(void)> f = &Foo::doSomething;
}
};
Je reçois error C2064: term does not evaluate to a function taking 0 arguments
dans xxcallobj
combiné à des erreurs d’instanciation de modèles étranges. Actuellement, je travaille sur Windows 8 avec Visual Studio 2010/2011 et sur Windows 7 avec VS10, il échoue également. L'erreur doit être basée sur des règles C++ étranges que je ne suis pas.
EDIT: Je fais PAS utiliser boost. C++ 11 est intégré au compilateur MS.
Une fonction membre non statique doit être appelée avec un objet. C'est-à-dire qu'il passe toujours implicitement "ce" pointeur comme argument.
Parce que votre signature std::function
indique que votre fonction ne prend aucun argument (<void(void)>
), vous devez bind le premier (et le seul) argument.
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
Si vous souhaitez lier une fonction avec des paramètres, vous devez spécifier des espaces réservés:
using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, _1, _2);
Ou, si votre compilateur prend en charge les lambdas C++ 11:
std::function<void(int,int)> f = [=](int a, int b) {
this->doSomethingArgs(a, b);
}
(Je n'ai pas de compilateur capable de gérer C++ 11 sous la main maintenant, je ne peux donc pas vérifier celui-ci.)
Soit vous avez besoin
std::function<void(Foo*)> f = &Foo::doSomething;
afin que vous puissiez l'appeler sur n'importe quelle instance ou que vous deviez lier une instance spécifique, par exemple this
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
Si vous avez besoin de stocker une fonction membre sans l'instance de la classe, vous pouvez faire quelque chose comme ceci:
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);
À quoi ressemblerait le type de stockage sans auto ? Quelque chose comme ça:
std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable
Vous pouvez également passer cette fonction de stockage à une liaison de fonction standard
std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call
Notes passées et futures: Une interface plus ancienne std :: mem_func existait, mais est depuis déconseillée. Une proposition existe, post C++ 17, pour rendre pointeur sur les fonctions membres appelable . Ce serait le bienvenu.
Vous pouvez utiliser des foncteurs si vous voulez un contrôle moins générique et plus précis sous le capot. Exemple avec mon API win32 pour transférer un message api d'une classe à une autre classe.
#include <windows.h>
class IListener {
public:
virtual ~IListener() {}
virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};
#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;
}
};
#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);
}
};
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);
}
};
Bonne chance et merci à tous pour le partage des connaissances.