Jump to content
  • Advertisement
Sign in to follow this  
KanonBaum

C++ Template Event system not working (i.e. Fondly Crashes)

This topic is 2517 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I wrote, what seemed to me, a nifty little event system (that reminded me of my Java background) in C++. However, it is crashing with no explanation as to why. Templates are NOT my strong point and they seem to be giving me a hell of a time. Any help would be great. Without further ado, here is the run-down:

When a key is pressed, it is bundled as an Event and then passed down throughout all the InputListeners (virtually identical to EventListeners).
In this case, the TestState class is a GameLogicState (i.e. a level here) as well as an InputListener for an event.

However, whenever I press any key, my game crashes. I place output breaks everywhere and it seems to dispatch the event just fine to the listeners but it never outputs the phrase "ANYTHING?" like it should in the TestState class (although the output tells me it was sent).

Then a few cycles later it crashes. However, if I comment the lines "v_event_listeners->onEvent(event);"in the EventPublisher, it never crashes (but of course no Events are broadcast to the listeners so it is for nothing....)

Below are the main files of the code that is (seemingly) causing it to crash.

----

Note: This code is for a "library-independent game framework" I've been working on in my own time. I've been testing bits and pieces on different libraries.

In this case I am using the Gosu library.
----

Event.h

#ifndef EVENT_H
#define EVENT_H
/**
* The Event class is an event that occurs and needs to be spread
* To any listener waiting for the event so they may respond appropriately.
* As a base class, it can be derived to add more features.
* However, as a generic, it can also be used effecienty exactly as it is.
*/
template<typename EVENT_DATA>
class Event
{
private:
/** The data to the Event*/
EVENT_DATA *data;

public:
/**
* Generic constructor. Sets the data to the event.
* preconditions: None
* postconditions: data is assigned to event_data
* throws: None
*/
Event(EVENT_DATA *event_data) {data = event_data;}

/**
* Virtual deconstructor.
* preconditions: None
* postconditions: The memory for Event is de-allocated.
* throws: None
*/
virtual ~Event() { delete data; data = 0;}

/**
* Returns pointer to the event's data.
* preconditions: None
* postconditions: pointer of EVENT_DATA is returned
* throws: None
*/
const EVENT_DATA *getData() const { return data; }
};
#endif


EventListener.h

#ifndef EVENT_LISTENER_H
#define EVENT_LISTENER_H
#include "Event.h"
#include <iostream>
/** Forward declaring EventPublisher class*/
class EventPublisher;
/**
* A EventListener follows the Observer design pattern such that
* a publisher will notify any registered listeners of arbitray events.
* As a base class, it needs to be derived so it can listen for
* specific events and act upon them appropriately. */
class EventListener
{
private:
/** The publisher that the listener waits for*/
// EventPublisher *event_publisher;

public:
/**
* Generic constructor. Assigns the publisher.
* precondsitions: None
* postconditions: None
* throws: None
*/
// EventListener(EventPublisher *publisher){ event_publisher = publisher; }
EventListener() { ; }

/**
* Virtual deconstructor. Allows child classes to use true polymorphism.
* preconditions: None
* postconditions: The memory for EventListener is de-allocated
* throws: None
*/
virtual ~EventListener(){;}

/**
* Whenever an event is published, the listener is notified with the event itself.
* This functions needs to be re-implemented.
* preconditions: None
* postconditions: The EventListener will respond to events
* throws: None
*/

template<typename EVENT_DATA>
virtual void onEvent(Event<EVENT_DATA>* event) { ; }
};
#endif


EventPublisher.h

#ifndef EVENT_PUBLISHER_H
#define EVENT_PUBLISHER_H
#include <vector>
#include "Event.h"
#include "NullEventListenerException.h"
#include <iostream>
/** Forward declare EventListener*/
class EventListener;
/**
* The EventPublisher class is used when an event needs to be invoked
* regardless if by an object or by another event. By storing EventListeners
* and then notifying to all (like a boradcast) this can be achieved */
class EventPublisher
{
private:
/** The collection of all listeners to the publisher*/
std::vector<EventListener*> v_event_listeners;
public:
/**
* Generic constructor for EventPublisher
* preconditions: None
* postconditions: None
* throws: None
*/
EventPublisher() { ; }

/**
* Virtual deconstructor. Allows child classes to use true polymorphism.
* preconditions: None
* postconditions: The memory for EventPublisher is de-allocated
* throws: None
*/
virtual ~EventPublisher() { ; }

/**
* This method adds an event listener to it's collection to be notified.
* (sometimes referred to as "views")
* preconditions: event_listener is not null
* postconditions: Adds a listener.
* throws: Throws NullEventListenerException if event_listener is null.
*/
void attach(EventListener *event_listener) //throw NullEventListenerException
{
if(event_listener)
v_event_listeners.push_back(event_listener);
else
{
throw (new NullEventListenerException() );
}
}

/**
* Any listeners are notified of an event.
* preconditions: Event is not null.
* postconditions: If Event is null, nothing. Otherwise, all listeners are notified.
* throws: None
*/
template<typename EVENT_DATA>
void notify(Event<EVENT_DATA>* event)
{
if(event == 0)
return;

for(int i = 0; i < v_event_listeners.size(); i++)
{
v_event_listeners->onEvent(event);
}

}
};
#endif


InputDevice.h

#ifndef INPUT_DEVICE_H
#define INPUT_DEVICE_H
/**
* InputDevice is the universal input class. All input passes need to be
* accessed via InputDevice for any object. To be able to read input, a
* class must be of InputListener.*/
#include <vector>
#include "InputListener.h"
#include "../Event/EventPublisher.h"
class InputDevice : public EventPublisher
{
public:
InputDevice() : EventPublisher() {;}
virtual ~InputDevice(){;}

void attach(InputListener* input_listener)
{
EventPublisher::attach(input_listener);
}

virtual void poll() { ; }
};
#endif


GosuInputDeviceAdapter.cpp ( to wrap framework around library specific code )

#include "GosuInputDeviceAdapter.h"

GosuInputDeviceAdapter::GosuInputDeviceAdapter(Gosu::Input *input)
: InputDevice()
{
gosu_input = input;
}

GosuInputDeviceAdapter::~GosuInputDeviceAdapter()
{ ; }
void GosuInputDeviceAdapter::buttonDown(Gosu::Button button)
{
v_buttons.push_back(button);
}

void GosuInputDeviceAdapter::poll()
{
for(int i = 0; i < v_buttons.size(); i++)
{
this->notify( new Event<Gosu::Button>(&v_buttons) );
}

v_buttons.clear();
}


TestState.h

#ifndef TEST_STATE_H
#define TEST_STATE_H
#include "../GameLogic/GameLogicState.h"
#include "../GosuAdapters/GosuGraphicsDeviceAdapter.h"
#include "../GosuAdapters/GosuInputDeviceAdapter.h"
class TestState : public GameLogicState, public InputListener
{
private:
Image *player_img;
Drawable::DrawableNode *player;

public:
TestState(GraphicsDevice *g, InputDevice *i);
~TestState();

void onUpdate(GameLogic *logic);

template<typename EVENT_DATA>
void onEvent(Event<EVENT_DATA>* event)
{
std::cout << "ANYTHING?" << std::endl; // <-- SHOULD PRINT THIS AT LEAST!!

/*Gosu::Button *button = dynamic_cast<Gosu::Button*>(event->getData());

if(button)
{
TransformableDraw *trans = dynamic_cast<TransformableDraw*>(player->getData());

if(*button == Gosu::kbLeft)
{
trans->setX( trans->getX() - 1 );
}
else if(*button == Gosu::kbRight)
{
trans->setX( trans->getX() + 1 );
}
else if(*button == Gosu::kbUp)
{
trans->setY( trans->getY() - 1 );
}
else if(*button == Gosu::kbDown)
{
trans->setY( trans->getY() + 1 );
}
}*/
}
};
#endif


TestState.cpp

#include "TestState.h"
#include <iostream>
TestState::TestState(GraphicsDevice *g, InputDevice *i)
: GameLogicState(), InputListener()
{
GosuGraphicsDeviceAdapter *g_adptr = static_cast<GosuGraphicsDeviceAdapter*>(g);

player_img = new Image(
g_adptr->getGosuGraphics(),
L"media/graphics/bens_game_tileset.png",
3*16, 0, 16, 16);

g_adptr->insert(player_img);

player = player_img->makeNode<TransformableDraw>();

i->attach(this);
}
TestState::~TestState()
{
delete player_img;
}
void TestState::onUpdate(GameLogic *logic)
{
TransformableDraw *trans = static_cast<TransformableDraw*>(player->getData());

trans->setX( trans->getX() + 0.5 ); // Every cycle, move the image slightly to the right
}

Share this post


Link to post
Share on other sites
Advertisement
You are using pointers everywhere, which is actually the problem here:

You don't specify what v_buttons is, it looks like a std:.vector though. In [color=#000088]void [color=#660066]GosuInputDeviceAdapter[color=#666600]::poll[color=#666600]() you create a new event with a pointer to a v_buttons entry. And directly after that you clear v_buttons.

Now there are a number of events pointing to invalid memory. Once that is accessed it should crash right away. As it's C++ it usually doesn't crash right away, but later along the way.

Try to get acquainted with RAII, store objects rather than pointer to objects (where applicable). Remember, if you point to something, make sure that something stays alive.

Share this post


Link to post
Share on other sites
I thought that at first but quickly assumed that all the events would be polled before the vector of buttons was cleared, but now that I think about it, the button events are triggered by the Gosu::Input class which is on a separate thread which would make a lot of sense.

I'll go back and fix that and let you know how it goes.

Are there other places where I shouldn't be making a pointer of? (Probably just doing it out of habit.)

Thanks for the feedback!

Share this post


Link to post
Share on other sites


template<typename EVENT_DATA>
virtual void onEvent(Event<EVENT_DATA>* event) { ; }

[/quote]
Which compiler do you use? Function templates cannot be virtual.

Share this post


Link to post
Share on other sites



template<typename EVENT_DATA>
virtual void onEvent(Event<EVENT_DATA>* event) { ; }


Which compiler do you use? Function templates cannot be virtual.
[/quote]

Xcode 4. Interestingly enough, as soon as I took out "virtual" it no longer crashes.

However, now it doesn't follow polymorphism and call the TestState::onEvent() function but the very base EventListener::onEvent() instead.

Which is not the result I wanted.

Share this post


Link to post
Share on other sites
I believe there is no satisfactory solution for your problem. Your base EventListener has to either now about all message types or all specific listener types. This can be automated via variadic templates, but you'll have to recompile when adding a new listener type or message type.

Share this post


Link to post
Share on other sites
I understand. Perhaps I'm looking at Events the wrong way? I've been browsing through articles trying to find a new outlook on events and such, but they're too specific to a class. I feel like there has to be a way in C++ to achieve some sort of "plug-n-play" event system.

Thanks for the help so far,

Share this post


Link to post
Share on other sites
I found this article and it seems to follow the similar design that I was headed towards, but instead of having just the function templated, they avoided virtual template problems by moving it up a level: to the listener class itself which makes more sense (because a listener should be listening for specific types...)

I'm going to follow that and get this working. Thanks for all the help!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!