1. 程式人生 > >描述wxWidgets中事件處理的類型轉化

描述wxWidgets中事件處理的類型轉化

format type() The invalid connect tracker efi AC RM

wxWidgets是一個比較常用的UI界面庫,我曾經試著使用wxWidgets寫一個UI編輯工具,在此期間,學習了一些wxWidgets的知識。我對wxWidgets的綁定(Bind)比較好奇,想知道,wxWidgets是如何知道,我Bind的函數,是需要什麽參數,所以查看了一些源代碼,這裏,將了解的知識寫出來:

首先,給出綁定的相關源代碼:

class wxEvtHandler : public wxObject, 
            public wxTrackable
{
    // These functions are used for old, untyped, event handlers and don‘t
    
// check that the type of the function passed to them actually matches the // type of the event. They also only allow connecting events to methods of // wxEvtHandler-derived classes. // // The template Bind() methods below are safer and allow connecting // events to arbitrary functions or functors -- but require compiler
// support for templates. // // Dynamic association of a member function handler with the event handler, // winid and event type void Connect(int winid, int lastId, wxEventType eventType, wxObjectEventFunction func, wxObject *userData = NULL, wxEvtHandler
*eventSink = NULL) { DoBind(winid, lastId, eventType, wxNewEventFunctor(eventType, func, eventSink), userData); } bool Disconnect(int winid, int lastId, wxEventType eventType, wxObjectEventFunction func = NULL, wxObject* userData = NULL, wxEvtHandler* eventSink = NULL) { return DoUnbind(winid, lastId, eventType, wxMakeEventFunctor(eventType, func, eventSink), userData); } // Bind a method of a class (called on the specified handler which must // be convertible to this class) object to an event: template <typename EventTag, typename Class, typename EventArg, typename EventHandler> void Bind(const EventTag &eventType, void (Class::*method)(EventArg &), EventHandler *handler, int winid = wxID_ANY, int lastId = wxID_ANY, wxObject* userData = NULL) { DoBind(winid, lastId, eventType, wxNewEventFunctor(eventType, method, handler), userData); } template <typename EventTag, typename Class, typename EventArg, typename EventHandler> bool Unbind(const EventTag &eventType, void (Class::*method)(EventArg&), EventHandler *handler, int winid = wxID_ANY, int lastId = wxID_ANY, wxObject* userData = NULL) { return DoUnbind(winid, lastId, eventType, wxMakeEventFunctor(eventType, method, handler), userData); } void wxEvtHandler::DoBind(int id, int lastId, wxEventType eventType, wxEventFunctor *func, wxObject *userData) { wxDynamicEventTableEntry *entry = new wxDynamicEventTableEntry(eventType, id, lastId, func, userData); // Check // ... if (!m_dynamicEvents) m_dynamicEvents = new DynamicEvents; // We prefer to push back the entry here and then iterate over the vector // in reverse direction in GetNextDynamicEntry() as it‘s more efficient // than inserting the element at the front. m_dynamicEvents->push_back(entry); // 該函數的以下部分可忽略 // Make sure we get to know when a sink is destroyed wxEvtHandler *eventSink = func->GetEvtHandler(); if ( eventSink && eventSink != this ) { wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink); if ( evtConnRef ) evtConnRef->IncRef( ); else new wxEventConnectionRef(this, eventSink); } } };

我只貼出一部分,與我描述相關的內容,當然,很多時候會有些多余,不過,對於不太懂的地方,可以略過,對於比較重要的地方,我會比較詳細的說明。wxWidgets使用的是通過模板來記錄類型,然後,再將類型還原回來。

這裏,我隨便從wxWidgets中貼出一個Bind使用:

MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
    wxPanel* mainPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
    mainPane->Bind(wxEVT_CHAR_HOOK, &MyFrame::OnKeyDown, this);
}
void MyFrame::OnKeyDown(wxKeyEvent& event)
{
    wxMessageBox(wxString::Format("KeyDown: %i\n", (int)event.GetKeyCode()));
    event.Skip();
}

根據wxWidgets源代碼,可以知道wxEVT_CHAR_HOOK是const wxEventTypeTag<wxKeyEvent>,從Bind函數,可以知道,我們可以從兩處知道OnKeyDown函數的參數是wxKeyEvent,一個是傳入的參數wxEVT_CHAR_HOOK,一個是OnKeyDown。根據傳參可以知道,實現只會和DoBind的wxEventType eventType,以及wxEventFunctor *func有關。wxDynamicEventTableEntry的構造函數,使用了eventType,以及func作為參數,那麽類型轉化會與wxDynamicEventTableEntry的實現有關嗎?下面參考wxDynamicEventTableEntry的實現:

struct wxDynamicEventTableEntry : public wxEventTableEntryBase
{
    wxDynamicEventTableEntry(int evType, int winid, int idLast,
        wxEventFunctor* fn, wxObject *data)
    : wxEventTableEntryBase(winid, idLast, fn, data),
      m_eventType(evType)
    { }

    // not a reference here as we can‘t keep a reference to a temporary int
    // created to wrap the constant value typically passed to Connect() - nor
    // do we need it
    int m_eventType;

private:
    wxDynamicEventTableEntry& operator=(const wxDynamicEventTableEntry&) = delete;
};

註意,這裏evtType的類型是int,再向上看,其實DoBind第三個參數wxEventType實際類型就是int,也就是說,攜帶參數類型是wxKeyEvent的參數只有傳入DoBind的wxEventFunctor *func。參看wxDynamicEventTableEntry的實現,這個結構甚至不是模板,所以不會攜帶類型信息。那麽唯一的希望,就只有

wxNewEventFunctor(eventType, func, eventSink)構造的func指針所指示的對象,在DoBind中的wxEvtHandler *eventSink = func->GetEvtHandler();,eventSink並不攜帶類型信息。

現在我們要查看wxNewEventFunctor函數的實現:

template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
inline wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler> *
    wxNewEventFunctor(const EventTag&, void (Class::*method)(EventArg&), EventHandler *handler)
{
    return new wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler>
    (method, handler);
}

以及wxEventFunctorMethod的實現:
// functor forwarding the event to a method of the given object
//
template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
class wxEventFunctorMethod : 
    public wxEventFunctor,
    private wxPrivate::HandlerImpl
    <
     Class,
     EventArg,
     wxIsPublicyDerived<Class, wxEvtHandler>::value != 0
    >
{
    public:
    void operator()(wxEvtHandler* handler, wxEvent& event) override
    {
        Class *realHandler = m_handler;
        if (!realHandler)
        {
        realHandler = this->ConvertFromEvtHandler(handler);

        // this is not supposed to happen but check for it nevertheless
        wxCHECK_RET(realHandler, "invalid event handler");
        }

        // the real (run-time) type of event is EventClass and we check in
        // the ctor that EventClass can be converted to EventArg, so this cast
        // is always valid
        (realHandler->*method)(static_cast<EventArg&>(event));
    }
};

請認真看一下上面的static_cast<EventArg&>,正是這裏,wxWiidgets將event類型轉變為了正確的函數需要的類型。

下面貼出,wxWidgets調用上述函數的地方:

// 下面簡略摘抄一下,調用處理事件(wxEvent)的函數的位置:
bool wxEvtHandler::SearchDynamicEventTable(wxEvent& event)
{
    DynamicEvents& dynamicEvents = *m_dynamicEvents;

    bool needToPruneDeleted = false;

    for (size_t n = dynamicEvents.size(); n; n--)
    {
    wxDynamicEventTableEntry* const entry = dynamicEvents[n-1];

    if (!entry)
    {
        needToPruneDeleted = true;
        continue;
    }

    if (event.GetEventType() == entry->m_eventType)
    {
        wxEvtHandler *handler = entry->m_fn->GetEvtHandler();
        if (!handler)
        handler = this;
        if (ProcessEventIfMatched(*entry, handler, event))
        {
        return true;
        }
    }
    }

    // 以下省略
};
bool wxEvtHandler::ProcessEventIfMatched(const wxEventTableEntryBase& entry,
            wxEvtHandler *handler,
            wxEvent& event)
{
    int tableId1 = entry.m_id,
    tableId2 = entry.m_lastId;

    // match only if the event type is the same and the id is either -1 in
    // the event table (meaning "any") or the event id  matches the id
    // specified in the event table either exactly or by falling into 
    // the range between first and last
    if ((tableId1 == wxID_ANY) ||
    (tableId2 == wxID_ANY && tableId1 == event.GetId()) ||
    (tableId2 != wxID_ANY &&
     (event.GetId() >= tableId1 && event.GetId() <= tableId2)))
    {
    event.Skip(false);
    event.m_callbackUserData = entry.m_callbackUserData;

#if wxUSE_EXCEPTIONS
    if (wxTheApp)
    {
        // call the handler via wxApp method which allows the user to catch
        // any exceptions which may be throw by any handler in the program
        // in one place
        wxTheApp->CallEventHandler(handler, *entry.m_fn, event);
    }
    else
#endif    // wxUSE_EXCEPTIONS
    {
        (*entry.m_fn)(handler, event);
    }

    if (!event.GetSkipped())
        return true;
    }

    return false;
}
// 下面再查看一下wxAppConsoleBase的CallEventHandler函數
void wxAppConsoleBase::CallEventHandler(wxEvtHandler *handler,
                    wxEventFunctor& functor,
                    wxEvent& event) const
{
    // If the functor holds a method then, for backward compatibility, call
    // HandleEvent()
    wxEventFunction eventFunction = functor.GetEvtMethod();

    if (eventFunction)
    HandleEvent(handler, eventFunction, event);
    else
    functor(handler, event);
}
void wxAppConsoleBase::HandleEvent(wxEvtHandler* handler, 
                   wxEventFunction func,
                   wxEvent& event) const
{
    // by default, simply call the handler
    (handler->*func)(event);
}

到這裏,應該說完了wxWidgets中處理事件類型中最主要的部分,這個也是C++模板用來保證類型安全的一個具體應用。C++模板是一種非常強大的工具,合理使用,可以使代碼得到很大的優化。本來,想要通過自己寫的一個簡單例子來進一步說明的,不過考慮這個例子已經比較長了,所以,寫在下一篇隨筆上面了。

 

描述wxWidgets中事件處理的類型轉化