Jump to content
  • Advertisement
Sign in to follow this  
andy1984

How to cut out duplicate code

This topic is 2119 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

How do I cut down on rewriting the same thing for each interface like IOnClosed, IOnFocus, IOnResized? If I want to change them, I don't want to have to change them all. They're all just the same,

class EventSystem
{
	protected:
		sf::Window* m_window;
	public:
		EventSystem(sf::Window* window);
		virtual ~EventSystem();
		void poll();

	class IOnClosed
	{
		protected:
			typedef std::list <IOnClosed*> List;
			static List s_list;
			List::iterator m_itr;
		public:
			IOnClosed()
			{
				m_itr = s_list.insert(s_list.begin(), this);
			};
			~IOnClosed()
			{
				s_list.erase(m_itr);
			};
			virtual void onClosed(const sf::Event& e) = 0;
			static void call(const sf::Event& e)
			{
				for (List::iterator itr = s_list.begin(); itr != s_list.end();itr++)
					(*itr)->onClosed(e);
			};
	};
	class IOnResized
	{
		protected:
			typedef std::list <IOnResized*> List;
			static List s_list;
			List::iterator m_itr;
		public:
			IOnResized()
			{
				m_itr = s_list.insert(s_list.begin(), this);
			};
			~IOnResized()
			{
				s_list.erase(m_itr);
			};
			virtual void onResized(const sf::Event& e) = 0;
			static void call(const sf::Event& e)
			{
				for (List::iterator itr = s_list.begin(); itr != s_list.end();itr++)
					(*itr)->onResized(e);
			};
	};
	class IOnLostFocus
	{
		protected:
			typedef std::list <IOnLostFocus*> List;
			static List s_list;
			List::iterator m_itr;
		public:
			IOnLostFocus()
			{
				m_itr = s_list.insert(s_list.begin(), this);
			};
			~IOnLostFocus()
			{
				s_list.erase(m_itr);
			};
			virtual void onLostFocus(const sf::Event& e) = 0;
			static void call(const sf::Event& e)
			{
				for (List::iterator itr = s_list.begin(); itr != s_list.end();itr++)
					(*itr)->onLostFocus(e);
			};
	};

Share this post


Link to post
Share on other sites
Advertisement

You can try to use the curiously recursive template pattern, which looks something like this:

 

template <class C>
class EventHandler {
public:
  typename std::list<C *>::iterator m_iterator;
  EventHandler() {
    m_iterator = C::s_list.insert(C::s_list.begin(), dynamic_cast<C *>(this));
  }
  
  ~EventHandler() {
    C::s_list.erase(m_iterator);
  }
 
public:  
  virtual void call(const sf::Event &e) {}
  
  static void call_all(const sf::Event &e) {
    for (auto on_event : C::s_list)
      on_event.call(e);
  }
};
 
class OnClosed : public EventHandler<OnClosed> {
public:
  static std::list<OnClosed *> s_list;
};
 
std::list<OnClosed *> OnClosed::s_list;
 
class OnResized : public EventHandler<OnResized> {
public:
  static std::list<OnResized *> s_list;
};
 
std::list<OnResized *> OnResized::s_list;
 
class OnLostFocus : public EventHandler<OnLostFocus> {
public:
  static std::list<OnLostFocus *> s_list;
};
 
std::list<OnLostFocus *> OnLostFocus::s_list;
Edited by Álvaro

Share this post


Link to post
Share on other sites

Or better yet, do not combine 2 completely separate concepts into single classes.  The responsibility to have manage a list of event handlers is a single purpose.  Aka a class.  This is a basic common pattern known by many names include Broadcaster / Listener, Publish / Subscribe or Observer pattern.  The responsibility to define a callback signature is another responsibility - usually using a function pointer typedef, a delegate, or other technique.  And the responsibility to define an actual specific slot, for a specific purpose within your program is a 3rd one.  So to put in some possible specifics.

 

Alvaro's EventHandler is a good class here - although for the way I plan to decompose responsibility in this example, I would rename something else, perhaps "EventManager" or something.

 

I would then use EventHandler or EventCallback as the base class for various event signatures, perhaps ClosedEventHandler, ResizedEventHandler, etc.  So a "ClosedEventHandler" would be a function signature for a callback that accepts the "closed event argument type" as its event argument.

 

I would then using INSTANCES of the EventHandler class, for each slot I had.  So perhaps:

 

EventManager<ClosedEventHandler> Closed;

EventManager<ResizedEventHandler> Resized;

 

And the OnClosed() method would iterate over the Closed EventManager calling all of the managed handlers (ie, "Raise" the event).

 

If you just look into how C#/.Net events and delegates work, you'll find a great example of a design like this.  But back in my C++ days I had written something quite similar to what I am posting here - although I haven't looked up all the details, and the names and such were different.

Share this post


Link to post
Share on other sites
I restricted myself to answering the specific question, but Xai is correct that those classes do too many things, and some of the semantics are questionable (and not thread safe). You may want to look into Boost.Signals2.

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!