views:

42

answers:

1

In the following code I do the following:

  • I create a new template wxCommandEvent class that holds some payload specified by the template.
  • I then subclass a wxStringPayloadEvent class.
  • Finally I create an example app that simply issues a wxStringPayloadEvent and then the event handler gets triggered and displays the payload in a message box on the screen.

If I use the slightly old-fashioned wxEvtHandler::Connect method, (as I've commented out below), then everything works. But if I use the wxEvtHandler::Bind method I get a series of very cryptic error messages (posted after the code).

Since Bind allows more freedom and is easier to use (it doesn't require creating awkward macros), then I would like to use it instead of Connect ... any ideas?

Here's the code:

#include <wx/wx.h>

//Creating my own custom event which will hold a payload of some templated type
template <class Payload_type>
class TemplatedPayloadEvent : public wxCommandEvent
{
public:
 TemplatedPayloadEvent(){}
 TemplatedPayloadEvent(wxEventType eventType) : wxCommandEvent(eventType){}

 TemplatedPayloadEvent(const TemplatedPayloadEvent& event)
 : wxCommandEvent(event)  
 {
  this->mPayload = event.mPayload;
 }

 virtual TemplatedPayloadEvent* Clone() const {return new TemplatedPayloadEvent(*this);}

 void setPayload(Payload_type payload) {mPayload = payload;}
 Payload_type getPayload() {return mPayload;}

private:
 Payload_type mPayload;
};


//instantiating new event along with associated elements
class wxStringPayloadEvent : public TemplatedPayloadEvent<wxString>
{
public:
 wxStringPayloadEvent() : TemplatedPayloadEvent<wxString>(wxEVT_STRING_PAYLOAD){};
};
typedef void (wxEvtHandler::*wxStringPayloadEventFunction)(wxStringPayloadEvent&);
#define wxStringPayloadEventHandler(func) \
 (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)\
 wxStaticCastEvent(wxStringPayloadEventFunction, &func)
const wxEventType wxEVT_STRING_PAYLOAD = wxNewEventType();


//implementing test application
class MyApp : public wxApp
{
public:
 virtual bool OnInit();
 void OnProcessCustom(wxStringPayloadEvent& event);
};

bool
MyApp::OnInit()
{
 //Connect event
 //Connect(wxEVT_STRING_PAYLOAD, wxStringPayloadEventHandler(MyApp::OnProcessCustom));
 Bind(wxEVT_STRING_PAYLOAD, &MyApp::OnProcessCustom, this);///< wish I could use this

    wxStringPayloadEvent eventCustom;
 eventCustom.SetEventObject(this);
 eventCustom.setPayload(wxT("Test payload."));
 wxPostEvent(this, eventCustom);

 return true;
}

void MyApp::OnProcessCustom(wxStringPayloadEvent& event)
{
 wxMessageBox(wxT("Event received.  Payload = \"") + event.getPayload() + wxT("\""));
}

IMPLEMENT_APP(MyApp);

Here's the error message:

/ThirdParty/Includes/wx/event.h: In constructor 'wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler>::wxEventFunctorMethod(void (Class::*)(EventArg&), EventHandler*) [with EventTag = int, Class = MyApp, EventArg = wxStringPayloadEvent, EventHandler = MyApp]':
/ThirdParty/Includes/wx/event.h:587:   instantiated from 'wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler>* wxNewEventFunctor(const EventTag&, void (Class::*)(EventArg&), EventHandler*) [with EventTag = int, Class = MyApp, EventArg = wxStringPayloadEvent, EventHandler = MyApp]'
/ThirdParty/Includes/wx/event.h:3182:   instantiated from 'void wxEvtHandler::Bind(const EventTag&, void (Class::*)(EventArg&), EventHandler*, int, int, wxObject*) [with EventTag = wxEventType, Class = MyApp, EventArg = wxStringPayloadEvent, EventHandler = MyApp]'
../src/program.cpp:29:   instantiated from here
/ThirdParty/Includes/wx/event.h:382: error: invalid conversion from 'wxEvent*' to 'wxStringPayloadEvent*'
/ThirdParty/Includes/wx/event.h:382: error:   initializing argument 1 of 'static void wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler>::CheckHandlerArgument(EventArg*) [with EventTag = int, Class = MyApp, EventArg = wxStringPayloadEvent, EventHandler = MyApp]'

Update more clues:

  • This might be a clue, but my C++ is still not good enough to be sure.
  • Looking at event.h:382 I find the following comment:

if you get an error here it means that the signature of the handler you're trying to use is not compatible with (i.e. is not the same as or a base class of) the real event class used for this event type

+1  A: 

This is probably something that should be documented, but as far as I can see, you can't just declare your event type using 'const wxEventType ...' when you're using Bind(). Bind() requires the event type class which is provided when you use wxDEFINE_EVENT() instead. So, you actually need to make a few changes here.

1) Use this to define your event type (replacing 'const wxEventType ...'):

wxDEFINE_EVENT(wxEVT_STRING_PAYLOAD, wxStringPayloadEvent);

2) The above needs to be done after wxStringPayloadEvent has been defined obviously, so your wxStringPayloadEvent constructors can't just default to your custom event type. So here's what you're constructors for wxStringPayloadEvent should look more like:

wxStringPayloadEvent() : TemplatedPayloadEvent<wxString>(){};
wxStringPayloadEvent(wxEventType eventType) : TemplatedPayloadEvent<wxString>(eventType){};

3) Since you can't default your event type, you'll need to specify it in your custom event object construction:

wxStringPayloadEvent eventCustom(wxEVT_STRING_PAYLOAD);

I've tested these changes with SVN trunk, and it seems to work beautifully.

Bryan Petty
Hey! You're magic! I had overlooked the need for wxDEFINE_EVENT in the documentation.
John Berryman