Add helper classes to improve callback handling.

This commit is contained in:
Mark de Wever 2010-03-30 09:12:17 +00:00
parent 453f3d5a8c
commit 49b298b8a5
3 changed files with 211 additions and 0 deletions

View file

@ -383,6 +383,7 @@ available space. The documentation of that algorithm is written in
doxygen\footnote{\url{http://devdocs.wesnoth.org/layout_algorihm.html}}.
\section{Event handling and dispatching}
\label{event_handling}
The event handling translates the ``raw'' SDL events to an event structure
specific to gui2, effectively decoupling the interface. This also allows adding
@ -499,5 +500,44 @@ travelling policy, but it won't get cheap}. So in the same spirit that the
standard library doesn't add operator[] for std::list I omitted the postfix
increment operator.
\section{Callbacks}
\S~\ref{event_handling} describes the generic event handling for the widgets but
in some cases a widget wants to notify other widgets of a state change. Parts of
gui2 use simple C-style callbacks for that purpose, but using boost::function
makes better replacement. Therefore the code was analysed closer and another
problem should be rectified; The callback causes a binding between two objects,
but they are not notified of the deletion of the object leaving a hole for
calling a destroyed object.
In order to fix the problem two classes are defined:
\begin{description}
\item[tnotifier] The class sending the wanted notification.
\item[tnotifiee] The class to manage the lifetime of the connection.
\end{description}
The tnotifiee is a small class that holds a pointer to the receiver its
connected to, upon destruction it uses this pointer to deregister itself by the
receiver. There after the callback function will no longer be called.
The class should be used as member of a class so it can manage the lifetime of
the connection with the tnotifier.
\paragraph{}
The tnotifier is the main class, when a callback is registered it stores the
callbacks in an internal list and updates the pointer in the tnotifiee to
itself.
Upon destruction it clears the pointer in all tnotifiees that point to use, that
way upon destruction of the tnotifiee it won't try to deregister itself with
this destroyed object.
Subclasses of the tnotifier should add an notification function so the notifier
can call all callbacks in the list. The notifier should take care of this
calling.
\end{document}

View file

@ -0,0 +1,56 @@
/* $Id$ */
/*
Copyright (C) 2010 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_NOTIFIEE_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_NOTIFIEE_HPP_INCLUDED
namespace gui2 {
template<class T>
class tnotifier;
/**
* Helper class to implement callbacks with lifetime management.
*
* This part manages the lifetime off the callback.
*/
template<class FUNCTOR>
class tnotifiee
{
public:
typedef FUNCTOR tfunctor;
friend class tnotifier<tfunctor>;
tnotifiee()
: notifier_(NULL)
{
}
~tnotifiee()
{
if(notifier_) {
notifier_->disconnect_notifiee(*this);
}
}
private:
/** Pointer the the tnotifier that's linked to us. */
tnotifier<tfunctor> *notifier_;
};
} //namespace gui2
#endif

View file

@ -0,0 +1,115 @@
/* $Id$ */
/*
Copyright (C) 2010 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GUI_WIDGETS_AUXILIARY_NOTIFIER_HPP_INCLUDED
#define GUI_WIDGETS_AUXILIARY_NOTIFIER_HPP_INCLUDED
#include "foreach.hpp"
#include "gui/auxiliary/notifiee.hpp"
#include <cassert>
#include <map>
namespace gui2 {
/**
* Helper class to implement callbacks with lifetime management.
*
* This part manages the connecting and disconnecting of the callbacks.
*
* Subclasses should implement a way to call all callback.
*/
template<class FUNCTOR>
class tnotifier
{
public:
typedef FUNCTOR tfunctor;
tnotifier()
: notifiees_()
{
}
~tnotifier()
{
typedef std::pair<tnotifiee<tfunctor>* const, tfunctor> thack;
foreach(thack& item, notifiees_) {
assert(item.first);
assert((*item.first).notifier_ == this);
(*item.first).notifier_ = NULL;
}
}
/**
* Connects a callback.
*
* @param notifiee The notifiee controlling the lifetime of
* the callback.
* @param functor The callback to call.
*/
void connect_notifiee(
tnotifiee<tfunctor>& notifiee
, tfunctor functor)
{
notifiees_.insert(std::make_pair(&notifiee, functor));
assert(!notifiee.notifier_);
notifiee.notifier_ = this;
}
/**
* Disonnects a callback.
*
* @param notifiee The notifiee controlling the lifetime of
* the callback. Uses since its address is an
* unique key.
*/
void disconnect_notifiee(tnotifiee<tfunctor>& notifiee)
{
typename std::map<tnotifiee<tfunctor>*, tfunctor>::iterator
itor = notifiees_.find(&notifiee);
if(itor != notifiees_.end()) {
assert(notifiee.notifier_ == this);
notifiee.notifier_ = NULL;
notifiees_.erase(itor);
}
}
/***** ***** ***** setters / getters for members ***** ****** *****/
const std::map<tnotifiee<tfunctor>*, tfunctor>& notifiees() const
{
return notifiees_;
}
private:
/** List of registered callbacks. */
std::map<tnotifiee<tfunctor>*, tfunctor> notifiees_;
};
} //namespace gui2
#endif