2012-10-02 186 views
0

我正在考虑创建一个使用Win32 API和C++标准库的Window类库
我想添加处理窗口的信号/插槽功能消息。将boost :: bind对象转换为函数指针 - 实现消息映射

using namespace std; 
using namespace boost::signals2; 
typedef signal<void (Window*, EventArgs*)> WndEvent; 

class EventArgs { public: HWND hWnd; WPARAM wParam; LPARAM lParam; }; 

class Window { 
    protected: unordered_map<UINT, WndMsg*> msgMap; 
    public: void addMsgHandler(UINT msg, void (*handler)(Window*, EventArgs*)) { 
     auto iter=msgMap.find(msg); 
     if(iter==msgMap.end()) { 
      WndEvent* newEvent = new WndEvent(); 
      newEvent->connect(handler); 
      msgMap.insert(make_pair(msg, newEvent)); 
     } 
     else iter->second->connect(handler); 
    } 

    private: LRESULT wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lparam) { 
     for(auto iter=msgMap.begin(); iter!=msgMap.end(); iter++) 
      iter->second->(); 
    } 
}; 

在使用该库:

class Form : public Window { 
    void initialize() {// add here pair of 'message' and 'corresponding handler' 
     addMsgHandler(WM_LBUTTONUP, boost::bind(&Form::onLButtonUp, this, _1, _2)); 
     ... 
    } 
    void onLButtonUp(Window* sender, EventArgs* e) { // event handler 
     wchar_t buf[1000]; 
     wsprintf(buf, L"(%d, %d) clicked", GET_X_LPARAM(e->lParam), GET_Y_LPARAM(e->lParam)); 
     MessageBox(0, buf, L"", MB_OK); 
    } 
} 

这里的问题是onLButtonUp的签名不匹配,因为它是一个类的成员函数。
所以,我想使用boost ::绑定,我得到了编译错误是这样的:

Window::addMsgHandler': cannot convert parameter 2 'boost::_bi::bind_t<R,F,L>' to 'void (__cdecl *)(Window *,EventArgs *)' 

,这意味着我必须要改变(?从简单的函数指针,以提高函数对象)addMsgHandler的签名(),但

我无法确定要传递的数据类型(具有很长的模板参数)。

编译错误消息称

R=void,    
F=boost::_mfi::mf2<void,Form,Window *,EventArgs *>,1>       
L=boost::_bi::list3<boost::_bi::value<Form*>,boost::arg<1>,boost::arg<2>>     

此外,由于许多其他类从Window类派生的,我不知道该模板类型时,我定义类窗口

有没有适当的方式来传递boost.bind对象作为函数参数?

+0

我不想存储函数指针,但信号。 boost.function可以存储信号吗? –

+0

恐怕我不明白你的意思,但是......然后,我应该将'msgMap'的类型从map更改为multimap,将函数添加到该multimap中,并且每次给出消息(WM_'s)时重新连接()所有的处理程序到相应的boost :: signal,并触发信号?这不奇怪吗?我认为应该为每个处理程序调用一次connect()。 –

+0

我首先想到的情况是这样的:在地图中存储connect()'ed信号,而不是函数本身 –

回答

0

将其定义是这样的:

void addMsgHandler(UINT msg, const function<void(Window* , EventArgs*)>& slot); 

甚至是这样的:

void addMsgHandler(UINT msg, const WndEvent::slot_type &slot); 

现在,调用这个函数时,slot参数应该是一个 “可赎回” 接受的类型Window * 2个参数和EventArgs *,并且您可以使用mem_fn,boost::bind,std::bind或仅传递适当的原始函数ptr来创建这样的插槽。

+0

谢谢。实现了第一个和应用程序的正常工作,即使是派生类Form。现在我可以像这样使用addMsgHandler:addMsgHandler(WM_LBUTTONUP,bind(&Form :: onLButtonUp,this,_1,_2)); –

+0

@杰弗里Goines ok,太好了。但在我看来,第二种形式更好,因为它使你的意图更清楚。如果你改变了WndEvent签名,'addMsgHandler'不需要改变。 –