1つのクラスについて、1つのmap
格納std::function
オブジェクトに、同じクラスのメンバー関数へのいくつかの関数ポインターを格納します。しかし、最初はこのコードで失敗します:
class Foo {
public:
void doSomething() {}
void bindFunction() {
// ERROR
std::function<void(void)> f = &Foo::doSomething;
}
};
error C2064: term does not evaluate to a function taking 0 arguments
のxxcallobj
に、奇妙なテンプレートのインスタンス化エラーが組み合わされています。現在、私はWindows 8でVisual Studio 2010/2011を使用していますが、Win 7でVS10を使用している場合も失敗します。エラーは、私が従わないいくつかの奇妙なC++ルールに基づいている必要があります。
編集:NOTブーストを使用します。これは、MSコンパイラに統合されたC++ 11です。
非静的メンバー関数は、オブジェクトを使用して呼び出す必要があります。つまり、引数として常に「this」ポインターを暗黙的に渡します。
std::function
シグネチャは、関数が引数(<void(void)>
)を受け取らないことを指定しているため、bindが最初の(そして唯一の)引数である必要があります。
std::function<void(void)> f = std::bind(&Foo::doSomething, this);
関数をパラメーターにバインドする場合は、プレースホルダーを指定する必要があります。
using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, _1, _2);
または、コンパイラがC++ 11ラムダをサポートしている場合:
std::function<void(int,int)> f = [=](int a, int b) {
this->doSomethingArgs(a, b);
}
(C++ 11対応のコンパイラは手元にありません今ですので、これをチェックすることはできません。)
どちらかが必要
std::function<void(Foo*)> f = &Foo::doSomething;
そのため、任意のインスタンスで呼び出すことができます。または、this
などの特定のインスタンスをバインドする必要があります
std::function<void(void)> f = std::bind(&Foo::doSomething, 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);
autoがない場合、ストレージタイプはどのようになりますか?このようなもの:
std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable
この関数ストレージを標準の関数バインディングに渡すこともできます
std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call
過去および将来の注意:古いインターフェースstd :: mem_funcが存在しましたが、廃止されました。 C++ 17以降、 呼び出し可能なメンバー関数へのポインター にする提案があります。これは大歓迎です。
あまり一般的ではなく、より正確な制御をボンネットの下で行いたい場合は、ファンクターを使用できます。 APIメッセージをクラスから別のクラスに転送するwin32 APIの例。
#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);
}
};
幸運を祈り、知識を共有してくれたすべての人に感謝します。