Bored of making listener functions


#1

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_

#2

I should add that i designed this to be a timesaver for quickly banging something together without doing the same old stuff getting in the way (esp. when it’s just for an experiment). I’m not sure how good an idea it would be to put it to heavy use, especially if you’re going to be using it in a base class.

i just wanted to see if it was possible to make a quick general listener template.


#3

Yeah, not a bad pattern, that. The only drawback I can see is that if some of the listener callbacks need extra parameters, you’d have trouble getting them into that sendMessage method without making them temporary member variables, which would be a bit of a mess. But certainly for a lot of cases, it’d work just fine.


#4

Isn’t libsigc++ (http://libsigc.sourceforge.net/) what you are looking for ?


#5

probably! :slight_smile: i wasn’t really looking for anything, it was just a personal exercise for a laugh.

i know… sad isn’t it! :shock: :shock: :? :oops: :wink:


#6