I’ve been getting sick and tired of always having to write the same listener functions when writing classes that interact with each other. addListener(.) removeListener(.)blah blah blah
I figured there ought to be a way to do it more quickly - it’s so annoying when you’re trying to flesh out a model and you have to do the same boring stuff. Plus, I like my class declarations to be as defined and free from clutter as possible.
So, I’ve quickly come up with this. It’s a bit experimental, but it seems to work. [size=150]I’d like to know if any of you think it’s stupid, or poor design. [/size]Or perhaps you might think it is actually quite useful.
It’s template based. If you have a class that should have listeners of some type, you just do the following:
[1] Declare your listener class. e.g.
class Frog;
class FrogListener
{
public:
virtual void frogCroaked (Frog* source, int volume)=0;
virtual void frogBlinked (Frog* source, bool aggressively)=0;
};
(this example wants to know where its come from, so i’ve of course included a forward declaration of the source class).
[2] Subclass ‘HasListeners’ using the listener type. E.g.
class Frog : public HasListeners<FrogListener>
{
public:
enum MessageIds
{
croakMessage = 0,
blinkMessage = 0
};
Frog ();
private:
void sendMessage (int type, FrogListener* target);
}
You define the body of sendMessage(type,target) to send a message to a single target - the message you send is determined by the type. If you only have one type of message you can ignore it, but it’s there to allow for different types. e.g.
void Frog::sendMessage (int type, FrogListener* target)
{
if (type == croakMessage) target->frogCroaked (this,currentVolume);
else if (type == blinkMessage) target->frogBlinked (this,angry);
}
When a message is triggered, this is called for all listeners.
To trigger a message to get sent to the listeners, you call triggerMessage with the type of message (i.e. from your enum)
Lastly, in the constructor for the HasListeners subclass, you can define whether or not the messages are handled asynchronously. By default they are, but you can make them synchronous like this:
Frog::Frog ()
: HasListeners<FrogListener>(false)
{
}
Anyway…
the code…
HasListeners.h
#ifndef _HASASYNCLISTENERS_H_
#define _HASASYNCLISTENERS_H_
#include "juce.h"
template <class ListenerType>
class HasListeners
{
public:
HasListeners<ListenerType> (bool async = true)
{
if (async)
{
asyncUpdater = new Async (this);
}
else asyncUpdater = 0;
}
~HasListeners<ListenerType> ()
{
if (asyncUpdater != 0)
{
asyncUpdater->cancelPendingUpdate ();
deleteAndZero (asyncUpdater);
}
listeners.clear ();
}
void addListener (ListenerType* listener)
{
jassert (listener != 0);
listeners.add (listener);
}
void removeListener (ListenerType* listener)
{
jassert (listener != 0);
listeners.removeValue (listener);
}
void triggerMessage (int type)
{
if (asyncUpdater!=0)
{
asyncUpdater->addMessageRequest (type);
}
else sendMessages (type);
}
private:
void sendMessages (int type)
{
listeners.lockArray();
for (int i=listeners.size()-1; i>=0; --i)
{
sendMessage (type, listeners.getUnchecked (i));
}
}
virtual void sendMessage (int type, ListenerType* target)=0;
class Async : public AsyncUpdater
{
public:
Async (HasListeners<ListenerType>* owner_)
{
owner = owner_;
}
void addMessageRequest (int type)
{
typesToSend.add (type);
triggerAsyncUpdate ();
}
void handleAsyncUpdate ()
{
typesToSend.lockSet ();
{
for (int i=typesToSend.size()-1; i>=0; --i)
{
int type = typesToSend.getUnchecked (i);
owner->sendMessages (type);
}
}
typesToSend.clearQuick ();
typesToSend.unlockSet ();
}
private:
HasListeners<ListenerType>* owner;
SortedSet<int,CriticalSection> typesToSend;
};
Async* asyncUpdater;
Array<ListenerType*,CriticalSection> listeners;
};
#endif//_HASASYNCLISTENERS_H_