Source: ../../../tse3/src/tse3/Notifier.h


Annotated List
Files
Globals
Hierarchy
Index
/*
 * @(#)Notifier.h 3.00 13 July 2000
 *
 * Copyright (c) 2000 Pete Goodliffe (pete@cthree.org)
 *
 * This file is part of TSE3 - the Trax Sequencer Engine version 3.00.
 *
 * This library is modifiable/redistributable under the terms of the GNU
 * General Public License.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; see the file COPYING. If not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef TSE3_NOTIFIER_H
#define TSE3_NOTIFIER_H

#include 

/******************************************************************************
 * This file contains the implementation of the TSE3 Notifier framework.
 * It is the TSE3 implementation of the observer design pattern (GoF book).
 *
 * The public API (the Notifier and Listener classes) for this framework can
 * be found in the bottom half of the file, the top half contains internal
 * implementation concerns.
 *
 * This implementation can cope with listener event methods with between
 * zero and four parameters. This can be extended, by adding extra code
 * at the positions marked with (*)
 *****************************************************************************/

/*
 * A few annoying presentation choices have been made in this file because
 * KDOC (2.0.36) falls over. Notably some short classes have been spread
 * out, and there is a ";" at the end of the Impl namespace.
 *
 * Other namespaces in the TSE3 library have a similar problem. In each
 * case the trailing ";" is intentional. Sigh.
 */

namespace TSE3
{
    template  class Listener;

    /**************************************************************************
     * Notifier implementation details: ignore this bit
     *************************************************************************/

    /**
     * Internal implementation namespace.
     *
     * You can ignore the classes in this namespace. They are internal
     * implementation details for the TSE3 library and are not part of the
     * normal public API.
     *
     * The contents include implementation details for the @ref Notifier class
     * and the @ref Impl::Mutex classes which provide TSE3 thread-safety.
     *
     * @short   TSE3 private implementation namespace
     * @author  Pete Goodliffe
     * @version 3.00
     * @see     Notifier
     * @see     Listener
     * @internal
     */
    namespace Impl
    {
        /**
         * This is a minimal default type. It is used by the @ref Event class
         * to represent 'no type specified'.
         *
         * @internal
         */
        class def_type
        {
        };

        /**
         * This class template is used as a compile time utility to count
         * how many parameters have been passed to the @ref Event class
         * template. For most types its value is 1. The @ref def_type is the
         * only exception, its value is 0.
         *
         * @internal
         */
        template 
        struct arg_count
        {
            enum { count=1 };
        };
        template <>
        struct arg_count
        {
            enum { count=0 };
        };

        /**
         * A type used to overload member functions based on integer
         * values. This is used in @ref Event to figure out which
         * @ref Event::invokeImpl member function to call.
         *
         * @internal
         */
        template
        class num_type
        {
        };

        /**
         * A very simple "vector" class. It holds a list of void *s. It
         * has enough vector-like methods that the Notifier framework can use
         * it.
         *
         * It is a disgusting thing to include in this library when we should
         * just be using std::vector instead. In fact, this class is
         * implemented with a std::vector. The reason for its inclusion is the
         * fact that it redces the size of libtse3.so.0.0.0 by about a third.
         * This can be accounted for by the reduction in size of link names.
         *
         * @internal
         */
        class void_list
        {
            public:

                void_list();
                void_list(const void_list &);
                ~void_list();

                /**
                 * Push a new void* onto the back of the list.
                 *
                 * You cannot insert a duplicate entry.
                 *
                 * @param p Elemnt to push
                 * @return Whether the insert was successful: if @p p
                 *         was already inserted, then false will
                 *         be returned.
                 */
                bool push_back(void *p);

                /**
                 * Returns true if @p p was in the void_list, false otherwise.
                 *
                 * @param  p Element to remove
                 * @return True if anything was erased
                 */
                bool erase(void *p);

                /**
                 * Returns the number of void*s in the void_list.
                 *
                 * @return Number of elements in list
                 */
                unsigned int size() const;

                /**
                 * Returns the void*s at index @p index
                 *
                 * @param index Index of item to retrieve
                 * @return void* at @p index
                 */
                void *operator[](unsigned int index);

                /**
                 * Returns whether or not @p p is in the void_list
                 *
                 * @param p Element to test
                 * @return Whether @p p is in the void_list
                 */
                bool contains(void *p) const;

            private:

                void_list &operator=(const void_list &);

                class impl;
                impl *pimpl;
        };

        /**
         * The Event class is an internal part of the Notifier/Listener
         * framework, used to multicast a particular event to a set of
         * Listener objects.
         *
         * It is a class that can 'hold' zero or more parameters. The upper
         * limit is set by the number of template parameters and
         * @ref invokeImpl member functions - more can be added if you really
         * need them.
         *
         * If a parameter is not needed, the template declaration will provide
         * @ref def_type which counts as 'no parameter'.
         *
         * @internal
         */
        template                            // (*)
        class Event
        {
            public:

                /**
                 * Ctor. You specify the member function of the
                 * @p interface_type class to call, and the parameters to call
                 * it with.
                 */
                explicit Event(listener_func func,
                               const p1_type &p1 = p1_type(),
                               const p2_type &p2 = p2_type(),
                               const p3_type &p3 = p3_type(),
                               const p4_type &p4 = p4_type())            // (*)
                    : func(func), p1(p1), p2(p2), p3(p3), p4(p4) {}      // (*)

                /**
                 * Calls 'func' on the specified set of @p listeners.
                 *
                 * This function delegates to the appropriate @ref invokeImpl
                 * method. Which one to call is worked out using the
                 * @ref arg_count utility.
                 */
                void callOnEvery(void_list &listeners)
                {
                    const unsigned int argCount = arg_count::count
                                                + arg_count::count
                                                + arg_count::count
                                                + arg_count::count;
                                                                         // (*)
                    void_list copy_list(listeners);
                    for (unsigned int i = 0; i < copy_list.size(); ++i)
                    {
                        if (listeners.contains(copy_list[i]))
                        {
                            typedef Listener listener_type;
                            interface_type *listener
                                = static_cast(copy_list[i]);
                            invokeImpl(listener, num_type());
                        }
                    }
                }

            private:

                /**
                 * There is a family of invokeImpl classes which will pass the
                 * parameters to the @p listener. Although we know that the
                 * callee is of type @p interface_type we abstract it as a
                 * template type (@p T) so that the functions are not
                 * instantiated by the compiler unless actually called.
                 *
                 * This is vital, since only one of the @ref invokeImpl
                 * functions will be valid for each event - only that one will
                 * be instantiated.
                 */
                template 
                void invokeImpl(T *listener, num_type<0>) const
                {
                    (void)(listener->*func)();
                }
                template 
                void invokeImpl(T *listener, num_type<1>) const
                {
                    (void)(listener->*func)(p1);
                }
                template 
                void invokeImpl(T *listener, num_type<2>) const
                {
                    (void)(listener->*func)(p1, p2);
                }
                template 
                void invokeImpl(T *listener, num_type<3>) const
                {
                    (void)(listener->*func)(p1, p2, p3);
                }
                template 
                void invokeImpl(T *listener, num_type<4>) const
                {
                    (void)(listener->*func)(p1, p2, p3, p4);
                }                                                        // (*)

                const listener_func  func;
                const p1_type       &p1;
                const p2_type       &p2;
                const p3_type       &p3;
                const p4_type       &p4;                                 // (*)
        };

    };

    /**************************************************************************
     * Public Notifier framework API
     *************************************************************************/

    /**
     * The Notifier template base class is an implementation of the
     * observer design pattern (GoF book).
     *
     * Objects that need to send out specific callback events derive from this
     * base class. Each Notifier class may have any number of callback events,
     * and each of these events may take any number (and type) of parameters.
     * The callback system is type safe and easy to use.
     *
     * Events sent from the Notifier class are received by objects of the
     * @ref Listener class.
     *
     * @sect Usage
     *
     * A separate interface class detailing each callback event and the
     * type of Notifier source class must be written, for example:
     * 
     *     class Exhibitionist;
     *
     *     class ExhibitionistListener
     *     {
     *         public:
     *             typedef Exhibitionist notifier_type;
     *
     *             virtual void eventOne(Exhibitionist *)        = 0;
     *             virtual void eventTwo(Exhibitionist *, int a) = 0;
     *     };
     * 
* The first parameter of callback events must always be a pointer to the * source object. * * You may choose to provide a default implementation for the callback * methods. This will save you having to reimplement every callback, only * the ones for the events you are interested in. * * You can now declare a @ref Notifier class thus: *
     *     class Exhibitionist : public Notifier
     *     {
     *         // Other contents....
     *     };
     * 
* and implement your functionality. * * The Exhibitionist class can send callbacks to any attached listener by * calling the @ref notify member function. For example: *
     *     notify(&ExhibitionistListener::eventTwo, 1);
     * 
* The source object pointer parameter is automatically provided and does * not need to be specified. * * To receive events from a Notifier class, you must write a class that * derives from @ref Listener. The documentation for @ref Listener * describes how to do this. * * @short Base class for objects that multicast events to listeners * @author Pete Goodliffe * @version 3.00 * @see Listener */ template class Notifier { public: /** * The type of @ref Listener that this Notifier class works with. */ typedef Listener listener_type; friend class listener_type; protected: /** * Creates a Notifier with no listeners. * * Use @ref Listener::attachTo to add listeners. * * You can only subclass this type, not instanitate it directly. */ Notifier() {} /** * The concrete Notifier type (i.e. the class that will * be subclassing Notifier. */ typedef typename interface_type::notifier_type c_notifier_type; /** * There is a family of @p notify functions. The allow a Notifier * to multicast an event to every attached @ref Listener object. * * They are overloaded on the different number of parameters each * listener event function takes. * * You call a notify method specifying first the member function of * the @p interface_type to call, and then the parameters to call * it with. * * Every listener function has a first parameter which is a pointer * to this (source) object. This is automatically provided by the * notify function, so you need not supply it. If a funciton takes * any extra parameters then you must specify them. * * The first notify function is for events with no extra * parameters. */ template void notify(func_type func) { typedef Impl::Event event_type; event_type(func, static_cast(this)) .callOnEvery(listeners); } /** * Notify function for events with one extra parameter. */ template void notify(func_type func, const p1_type &p1) { typedef Impl::Event event_type; event_type(func, static_cast(this), p1) .callOnEvery(listeners); } /** * Notify function for events with two extra parameters. */ template void notify(func_type func, const p1_type &p1, const p2_type &p2) { typedef Impl::Event event_type; event_type(func, static_cast(this), p1, p2) .callOnEvery(listeners); } /** * Notify function for events with three extra parameters. */ template void notify(func_type func, const p1_type &p1, const p2_type &p2, const p3_type &p3) { typedef Impl::Event event_type; event_type (func, static_cast(this), p1, p2, p3) .callOnEvery(listeners); } // (*) /** * The dtor tells every attached @ref Listener that this object * is being deleted. */ virtual ~Notifier() { // LOCK for (unsigned int i = 0; i < listeners.size(); ++i) { listener_type *l = static_cast(listeners[i]); l->NotifierImpl_Deleted (static_cast(this)); } } unsigned int numListeners() const { return listeners.size(); } private: /** * Attach a listener to this Notifier object. * * You can only attach a given listener object once - any * subsequent attempt to attach it results in nothing happening; * it is not an error, but the object will still only ever receive * one copy of any event. * * This function is only used by the @ref Listener class' * @ref Listener::attachTo method. * * @param listener The listener to attach * @return Whether the attach was successful: if the * @p listener_type was already attached, then false will * be returned. */ bool attach(listener_type *listener) { // LOCK return listeners.push_back(listener); } /** * Detach a listener from this Notifier object. * * If the listener is not already attached no error is raised. * * This function is only used by the @ref Listener class' * @ref Listener::detachFrom method. * * @param listener The listener to detach */ void detach(listener_type *listener) { // LOCK listeners.erase(listener); } typedef Notifier self_type; Notifier(const self_type &); self_type &operator=(const self_type &); Impl::void_list listeners; }; /** * The Listener class receives events sent from a @ref Notifier class. * * Listener inherits from the specified @p interface_type so you don't need * to mupltiply inherit from both Listener the @p interface_type in your * derived class. * * To instatiate the Listener class inherit from Listener. * Following the example in the @ref Notifier documentation, this would * be: *
     *     class Voyeur : public Listener
     *     {
     *         public:
     *             virtual void eventOne(Exhibitionist *src)        { ... }
     *             virtual void eventTwo(Exhibitionist *src, int a) { ... }
     *             virtual void Notifier_Deleted(Exhibitionist *src)
     *             {
     *                 // The 'src' is being deleted - you do not need to
     *                 // detach from it, the link is automatically broken.
     *
     *                 // This method is not defined in the
     *                 // ExhibitionistListener base class, but is defined by
     *                 // the Listener class. You
     *                 // don't have to implement it if you don't need it.
     *             }
     *     };
     * 
* * Upon deletion, the Listener class ensures that it is detached from every * @ref Notifier it has been attached to. This prevents a @ref Notifier * later trying to callback to an invalid object. * * @short Listener interface for @ref Notifier events * @author Pete Goodliffe * @version 3.00 * @see Notifier */ template class Listener : public interface_type { public: /** * The type of @ref Notifier class this listener works with. */ typedef Notifier notifier_type; /** * Attaches this Listener to @p notifier. * * You can only attach to a @ref Notifer object once - any * subsequent attempt to attach to it results in nothing happening; * it is not an error, but the Listener will still only ever * receive one copy of any event. * * @param notifier The @ref Notifier object to attach to */ void attachTo(notifier_type *notifier) { if (notifier->attach(this)) notifiers.push_back(notifier); } /** * Detaches this Listener from @p notifier. * * If the listener is not already attached to this @ref Notifier no * error is raised. * * @param notifier The @ref Notifier object to detach from */ void detachFrom(notifier_type *notifier) { if (notifiers.erase(notifier)) notifier->detach(this); } friend class notifier_type; protected: /** * Creates a new Listener which is not attached to any * @ref Notifier. * * Use @ref attachTo to attach to @ref Notifiers. * * You can only subclass this type, not instanitate it directly. */ Listener() {} /** * The concrete Notifier type (i.e. the class that derives from * Notifier. */ typedef typename interface_type::notifier_type c_notifier_type; /** * This may be implemented by Listener classes if they care about * a @ref Notifier object being deleted. * * It is called by the @ref Notifer destructor - so the @p notifier * is in the process of being deleted. Therefore it is not safe to * call methods on the event source object any more. * * You do not need to @ref detachFrom the the @ref Notifier * since the link is automatically broken for you. * * @param notifier The @ref Notifier that has been deleted */ virtual void Notifier_Deleted(c_notifier_type * /*notifier*/) {} /** * The destructor ensures that the Listener is detached from every * @ref Notifier it has been attached to. * * This means that you don't have to specifically detach from all * @ref Notifier sources yourself, although it is still considered * good practice to do so. */ virtual ~Listener() { // LOCK for (unsigned int i = 0; i < notifiers.size(); ++i) { notifier_type *n = static_cast(notifiers[i]); n->detach(this); } } private: /** * This is called by the corresponding @ref Notifier type's * destructor. * * This ensures that if a notifier_type that the Listener is * attached to is deleted, it is removed from the notifiers list, * so we will never attempt to detach from it later. * * It also calls the internal @ref Notifier_Deleted method. */ void NotifierImpl_Deleted(c_notifier_type *src) { notifiers.erase(static_cast(src)); this->Notifier_Deleted(src); } typedef Listener self_type; Listener(const self_type &); self_type &operator=(const self_type &); Impl::void_list notifiers; }; } #endif

Generated by: pete on philemon on Wed May 25 14:39:52 2005, using kdoc 2.0a54.