interfaces.h

Go to the documentation of this file.
00001 /***************************************************************************
00002                           interfaces.h  -  description
00003                              -------------------
00004     begin                : Fre Feb 28 2003
00005     copyright            : (C) 2003 by Martin Witte
00006     email                : witte@kawo1.rwth-aachen.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #ifndef KRADIO_INTERFACES_H
00019 #define KRADIO_INTERFACES_H
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <qptrlist.h>
00026 #include <qmap.h>
00027 #include <kdebug.h>
00028 #include <typeinfo>
00029 
00030 /*
00032 
00033    Interfaces - Our Concept
00034 
00035    Without connection management an interface can be defined easily as empty
00036    abstract C++-Class. But that's not what we want.
00037 
00038    Our interfaces also provide connection management. Thus each interface has
00039    exactly one matching counterpart, the complementary interface (cmplIF).
00040    Therefore connecting two objects that have matching interfaces can be
00041    automated.
00042 
00043    Our interfaces have to be able to support the following "functions":
00044 
00045    - send and receive messages (e.g. notifications, commands, ...) to
00046      all connected interfaces. These functions do not need a return value,
00047      but in some cases the sender might want to know if anyone has received
00048      his message. Thus a boolean return value should indicate if the message
00049      was handled or ignored.
00050 
00051    - query for information on connected interfaces / answer queries. These
00052      functions usually have a return value. A query is only executed on the
00053      "current" or - if not selected - the first or only connection.
00054 
00056 
00057    Why are we not using QT signal/slots?
00058 
00059    First the idea of using qt for connecting interfaces is very nice, as the
00060    signal/slot model is well known and hopefully properly implemented.
00061 
00062    But there are some problems:
00063 
00064    - Signals/slots do not support return values, except "call by reference".
00065      To provide queries or a delivery feedback for messages, wrapper functions
00066      would have been necessary.
00067 
00068    - Qt does not support multiple inheritance of QObjects. Thus even signals
00069      have to be declared abstract by the interface though the (later)
00070      implementation is already known.
00071 
00072      Those functions have to be declared as signals in the interface
00073      implementation (derived from QObject) though the implementation does not
00074      want to worry about these signals.
00075 
00076    - Qt does connect functions (signals/slots) and not interfaces. These
00077      functions have to be connected separately. By that it is possible to
00078      forget to connect signals/slots of that interfaces.
00079 
00080    - Aggregation of multiple interface implementations (each one is an QObject)
00081      is not possible because qt does not allow multiple inheritance of QObjects
00082 
00084 
00085    What about our own solution?
00086 
00087    Well, it eliminates at least the qt-problems explained above. But first we
00088    need a common mechanism to manage interface connections. This functionality
00089    can be provided by a common base class "InterfaceBase". It stores all
00090    connected interfaces in a list of InterfaceBase pointers, e.g. QPtrList.
00091 
00092    With this approach we would have some problems:
00093 
00094    - When calling a function of a connected interface a slow dynamic_cast
00095      is necessary to upcast the stored InterfaceBase pointer to the
00096      apropriate type.
00097 
00098    - Multiple inheritance of InterfaceBase must not be virtual. Otherwise
00099      interface connection management is mixed between interfaces.
00100      (well, virtual inheritance is usually no real issue, but worth a hint;-)
00101 
00102    To avoid these problems, InterfaceBase is a template with two parameters,
00103    thisIF (IF = interface) and cmplIF (complementary IF). With that
00104    information the base class for an interface is capable to handle
00105    connections with the correct type information. Additionally some pseudo
00106    types are declared (thisInterface, cmplInterface, IFList, IFIterator) to
00107    make easy-to-use macros for messages and queries possible.
00108 
00110 
00111    How do I use it ?   - Declarations
00112 
00113    First you have to declare the two matching interface-classes as unkown
00114    classes, because both their names are used in the class declarations.
00115    Afterwards you can declare both classes as class derived from
00116    InterfaceBase.
00117 
00118        class Interface;
00119        class ComplementaryInterface;
00120 
00121        class Interface : public InterfaceBase<Interface, ComplementaryInterface>
00122        {
00123            ...
00124        };
00125 
00126        class ComplementaryInterface : public InterfaceBase<ComplementaryInterface, Interface>
00127        {
00128            ...
00129        };
00130 
00131    With macro abbreviation:
00132 
00133        INTERFACE(Interface, ComplementaryInterface)
00134        {
00135        };
00136 
00137        INTERFACE(ComplementaryInterface, Interface)
00138        {
00139        };
00140 
00141 
00142    In order to receive/send Messages or query/answer queries we have to declare
00143    special methods:
00144 
00145    - sending Messages
00146 
00147      Declare a virtual constant method with return value "int" and the desired
00148      parameters. The return value will indicate how many receivers have handled
00149      the message:
00150 
00151          virtual bool  SendingMessages(int any_or_non_param) const;
00152 
00153      Abbreviation by macros:
00154 
00155           IF_SENDER(    SendingMessages(int any_or_non_param)   )
00156 
00157 
00158    - receiving Messages
00159 
00160      Declare an abstract Method with return value "bool", and the desired
00161      paramters. The return value indicates wether the message was handled or not:
00162 
00163          virtual bool  ReceivingMessages(int any_or_non_param) = 0;
00164 
00165      Abbreviation by macros:
00166 
00167           IF_RECEIVER(  ReceivingMessages(int any_or_non_param)   )
00168 
00169 
00170      The method has to be implemented by a derived class. The current item of the
00171      receivers conntions list is set to the sender.
00172 
00173 
00174    - querying queries
00175 
00176      Declare a virtual constant method with the desired return value and
00177      parameters:
00178 
00179           virtual int   QueryingQueries(int another_param) const;
00180 
00181      Abbreviation by macros:
00182 
00183          IF_QUERY(     int QueryingQueries(int another_param)        )
00184 
00185 
00186    - answering queries
00187 
00188      Declare an abstract Method with return value void, and the desired
00189      paramters:
00190 
00191          virtual void  AnsweringQueries(int another_param) = 0;
00192 
00193      Abbreviation by macros:
00194 
00195           IF_ANSWER(    AnsweringQueries(int another_param)   )
00196 
00197      The method has to be implemented by a derived class. The current item of the
00198      receivers conntions list is set to the sender.
00199 
00200 
00201    At last a note on maxConnections. This member is set on initialization by
00202    the constructor and thus can be set in a derived class in it's own
00203    constructor. Negative values are interpreted as "unlimited".
00204 
00205 
00207 
00208    How do I use it ?   - Implementations
00209 
00210    Because we do not have a MOC as Qt does, we have to implement our sending
00211    or querying methods by hand. But this minor disadvantage should be
00212    considered as less important than the fact, that this implementation is
00213    done where it belongs to. Especially because there are easy to use macros
00214    to do this:
00215 
00216    int   ComplementaryInterface::SendingMessages(int any_or_non_param) const
00217    {
00218        IF_SEND_MESSAGE( ReceivingMessages(any_or_non_param)  )
00219        // macro includes "return #receivers"
00220    }
00221 
00222    int   ComplementaryInterface::QueryingQueries(int another_param) const
00223    {
00224        IF_SEND_QUERY( AnsweringQuery(another_param), (int)"default return value" )
00225    }
00226 
00227 
00228    Even shorter:
00229 
00230    IF_IMPL_SENDER(  ComplementaryInterface::QueryingQueries(int param),
00231                     AnsweringQueries(param)
00232                  )
00233 
00234    IF_IMPL_QUERY(  int ComplementaryInterface::SendingMessages(int param),
00235                    ReceivingMessages(param),
00236                    (int)"default return value"
00237                 )
00238 
00240 
00241    How do I use it ?   - Disconnect/Connect notifications
00242 
00243 
00244    Usually the virtual methods notifyDisconnect(ed) or notifyConnect(ed)
00245    will be called within connect/disconnect methods.
00246 
00247    As constructors and destructors are not able to call virtual methods
00248    of derived classes, there are two possible problems:
00249 
00250    * Constructors: Calling a connect method in a constructor will not result
00251      in a connect notification of any derived class. Thus do not use connect
00252      calls in contructors if any derived class hast to receive all
00253      connect/disconnect notifications.
00254 
00255    * Destructors: If connections are still present if the interface destructor
00256      is called, it will only call its own empty noticedisconnect method. That
00257      shouldn't be a big problem as the derived class is already gone and
00258      doesn't have any interest in this notification any more. But it might be
00259      possible that the connected object wants to call a function of the just
00260      destroyed derived class. That is not possible. Dynamic casts to the
00261      derived class will return NULL. Do not try to call methods of this class
00262      by use of cached pointers.
00263 
00264 
00265 
00267 
00268    Extending and Aggregating Interfaces
00269 
00270    Our interfaces must be extended by aggregation. The reason is that
00271    otherwise we would have the same problems as with a common base class
00272    for connection management. Each interface extensions is an normal
00273    interface on its own.
00274 
00275    Example:
00276 
00277    class I_AM_FM_Radio : public IRadioBase,
00278                          public IRadioFrequencyExtension,
00279                          public IRadioSeekExtension
00280    {
00281        ...
00282    };
00283 
00284    To guarantee, that connection management continues to work, we have to overwrite
00285    the connect and disconnect methods:
00286 
00287      virtual bool I_AM_FM_Radio::connect (Interface *i) {
00288          IRadioBase::connect(i);
00289          IFrequencyExtension::connect(i);
00290          ISeekExtension::connect(i);
00291      }
00292 
00293      virtual bool I_AM_FM_Radio::disconnect (Interface *i) {
00294          IRadioBase::disconnect(i);
00295          IFrequencyExtension::disconnect(i);
00296          ISeekExtension::disconnect(i);
00297      }
00298 
00299 */
00300 
00301 
00303 
00304 // a polymorphic and *virtual* base class so that we can make use of
00305 // dynamic_casts in connect/disconnect and to be able to merge
00306 // connect/disconnect methods to one single function in case of multiple
00307 // inheritance
00308 
00309 class Interface
00310 {
00311 public:
00312     Interface () {}
00313     virtual ~Interface() {}
00314 
00315     virtual bool     connectI   (Interface *) { return false; }
00316     virtual bool     disconnectI(Interface *) { return false; }
00317 
00318     // "Interface &"-Versions for convienience, not virtual, only "Interface*"
00319     // versions have to / may  be overwritten in case of multiple inheritance
00320     bool     connectI   (Interface &i) { return connectI    (&i); }
00321     bool     disconnectI(Interface &i) { return disconnectI (&i); }
00322 };
00323 
00325 
00326 template <class thisIF, class cmplIF>
00327 class InterfaceBase : virtual public Interface
00328 {
00329 private:
00330     typedef InterfaceBase<thisIF, cmplIF>  thisClass;
00331     typedef InterfaceBase<cmplIF, thisIF>  cmplClass;
00332 
00333 //    friend class cmplClass; // necessary for connects (to keep number of different connect functions low)
00334 
00335 public:
00336 
00337     typedef thisIF                    thisInterface;
00338     typedef cmplIF                    cmplInterface;
00339 
00340     typedef QPtrList<cmplIF>          IFList;
00341     typedef QPtrListIterator<cmplIF>  IFIterator;
00342 
00343     typedef thisClass BaseClass;
00344 
00345 public :
00346     InterfaceBase (int maxIConnections = -1);
00347     virtual ~InterfaceBase ();
00348 
00349     // duplicate connects will add no more entries to connection list
00350     virtual bool     connectI(Interface *i);
00351     virtual bool     disconnectI(Interface *i);
00352 
00353 protected:
00354     virtual void     disconnectAllI();
00355 
00356 
00357 public:
00358 
00359     // It might be compfortable to derived Interfaces to get an argument
00360     // of the Interface class, but that part of the object might
00361     // already be destroyed. Thus it is necessary to evaluate the additional
00362     // pointer_valid argument. A null pointer is not transmitted, as the
00363     // pointer value might be needed to clean up some references in derived
00364     // classes
00365     virtual void     noticeConnectI     (cmplInterface *, bool /*pointer_valid*/) {}
00366     virtual void     noticeConnectedI   (cmplInterface *, bool /*pointer_valid*/) {}
00367     virtual void     noticeDisconnectI  (cmplInterface *, bool /*pointer_valid*/);
00368     virtual void     noticeDisconnectedI(cmplInterface *, bool /*pointer_valid*/) {}
00369 
00370     virtual bool     isIConnectionFree() const;
00371     virtual unsigned connectedI()        const { return iConnections.count(); }
00372 
00373     thisIF *initThisInterfacePointer();
00374     thisIF *getThisInterfacePointer()     const { return me; }
00375     bool    isThisInterfacePointerValid() const { return me_valid; }
00376     bool    hasConnectionTo(cmplInterface *other) const { return iConnections.containsRef(other); }
00377     void    appendConnectionTo(cmplInterface *other)    { iConnections.append(other); }
00378     void    removeConnectionTo(cmplInterface *other)    { iConnections.removeRef(other); }
00379 
00380 protected :
00381 
00382     IFList iConnections;
00383     int    maxIConnections;
00384 
00385  // functions for individually selectable callbacks
00386 protected:
00387     bool addListener   (const cmplInterface *i, QPtrList<cmplInterface> &list);
00388     void removeListener(const cmplInterface *i, QPtrList<cmplInterface> &list);
00389     void removeListener(const cmplInterface *i);
00390 
00391     QMap<const cmplInterface *, QPtrList<QPtrList<cmplInterface> > >  m_FineListeners;
00392 
00393 private:
00394     thisInterface *me;
00395     bool           me_valid;
00396 };
00397 
00398 
00399 // macros for interface declaration
00400 
00401 #define INTERFACE(IF, cmplIF) \
00402     class IF; \
00403     class cmplIF; \
00404     class IF : public InterfaceBase<IF, cmplIF> \
00405 
00406 #define IF_CON_DESTRUCTOR(IF, n) \
00407     IF() : BaseClass((n)) {} \
00408     virtual ~IF() { }
00409 
00410 // macros to make sending messages or queries easier
00411 
00412 
00413 // debug util
00414 #ifdef DEBUG
00415     #include <iostream>
00416     using namespace std;
00417     #define IF_QUERY_DEBUG \
00418         if (iConnections.count() > 1) { \
00419             kdDebug() << "class " << typeid(this).name() << ": using IF_QUERY with #connections > 1\n"; \
00420         }
00421 #else
00422     #define IF_QUERY_DEBUG
00423 #endif
00424 
00425 
00426 
00427 // messages
00428 
00429 #define SENDERS   protected
00430 #define RECEIVERS public
00431 
00432 #define IF_SENDER(decl) \
00433         virtual int decl const;
00434 
00435 #define IF_SEND_MESSAGE(call) \
00436         int ____n = 0; \
00437         for (IFIterator i(iConnections); i.current(); ++i) {   \
00438             if (i.current()->call ) ++____n; \
00439         }  \
00440         return ____n;
00441 
00442 #define IF_IMPL_SENDER(decl, call) \
00443         int decl const \
00444         { \
00445             IF_SEND_MESSAGE(call) \
00446         }
00447 
00448 #define IF_RECEIVER(decl) \
00449         virtual bool decl = 0;
00450 
00451 #define IF_RECEIVER_EMPTY(decl) \
00452         virtual bool decl { return false; }
00453 
00454 // queries
00455 
00456 #define ANSWERS public
00457 #define QUERIES protected
00458 
00459 #define IF_QUERY(decl) \
00460         virtual decl const;
00461 
00462 #define IF_SEND_QUERY(call, default) \
00463         cmplInterface *o = IFIterator(iConnections).current(); \
00464         if (o) { \
00465             IF_QUERY_DEBUG \
00466             return o->call; \
00467         } else { \
00468             return default; \
00469         } \
00470 
00471 #define IF_IMPL_QUERY(decl, call, default) \
00472         decl const { \
00473             IF_SEND_QUERY(call, default) \
00474         }
00475 
00476 #define IF_ANSWER(decl) \
00477         virtual decl = 0;
00478 
00479 
00480 
00481 
00483 // MACROS for individually selectable callbacks
00485 
00486 
00487 #define IF_SENDER_FINE(name, param) \
00488 protected: \
00489     int  name param const; \
00490 public: \
00491     bool register4_##name  (cmplInterface *); \
00492     void unregister4_##name(cmplInterface *); \
00493 private: \
00494     QPtrList<cmplInterface> m_Listeners_##name;\
00495 
00496 
00497 #define IF_SEND_MESSAGE_FINE(name, params, call) \
00498         int ____n = 0; \
00499         for (QPtrListIterator<cmplInterface> ____it(m_Listeners_##name); ____it.current(); ++____it) {   \
00500             if (____it.current()->call ) ++____n; \
00501         }  \
00502         return ____n;
00503 
00504 #define IF_IMPL_SENDER_FINE(class, name, param, call) \
00505     int class::name param const { \
00506         IF_SEND_MESSAGE_FINE(name, param, call) \
00507     } \
00508     \
00509     bool class::register4_##name(cmplInterface *i) {   \
00510         return addListener(i, m_Listeners_##name); \
00511     } \
00512     void class::unregister4_##name(cmplInterface *i) {   \
00513         m_Listeners_##name.remove(i);             \
00514     }
00515 
00516 
00518 
00519 
00520 template <class thisIF, class cmplIF>
00521 InterfaceBase<thisIF, cmplIF>::InterfaceBase(int _maxIConnections)
00522   : maxIConnections(_maxIConnections),
00523     me(NULL),
00524     me_valid(false)
00525 {
00526 }
00527 
00528 
00529 template <class thisIF, class cmplIF>
00530 InterfaceBase<thisIF, cmplIF>::~InterfaceBase()
00531 {
00532     me_valid = false;
00533     // In this state the derived interfaces may already be destroyed
00534     // so that dereferencing cached upcasted me-pointers in noticeDisconnect(ed)
00535     // will fail.
00536     // Thus we must ensure that disconnectAll() is called in the (upper) thisIF
00537     // destructor, not here (see macro IF_CON_DESTRUCTOR).
00538     // If this has not taken place (i.e. the programmer forgot to do so)
00539     // we can only warn, clear our list now and hope that nothing
00540     // more bad will happen
00541 
00542     if (iConnections.count() > 0) {
00543         thisClass::disconnectAllI();
00544     }
00545 }
00546 
00547 
00548 template <class thisIF, class cmplIF>
00549 bool InterfaceBase<thisIF, cmplIF>::isIConnectionFree () const
00550 {
00551     int m = maxIConnections;
00552     return  (m < 0) || (iConnections.count() < (unsigned) m);
00553 }
00554 
00555 template <class thisIF, class cmplIF>
00556 thisIF *InterfaceBase<thisIF, cmplIF>::initThisInterfacePointer()
00557 {
00558     if (!me) me = dynamic_cast<thisIF*>(this);
00559     me_valid = me != NULL;
00560     return me;
00561 }
00562 
00563 template <class thisIF, class cmplIF>
00564 bool InterfaceBase<thisIF, cmplIF>::connectI (Interface *__i)
00565 {
00566     // cache upcasted pointer, especially important for disconnects
00567     // where already destructed derived parts cannot be reached with dynamic casts
00568     initThisInterfacePointer();
00569 
00570     // same with the other interface
00571     cmplClass *_i = dynamic_cast<cmplClass*>(__i);
00572     if (!_i) {
00573         return false;
00574     }
00575 
00576     cmplIF    *i = _i->initThisInterfacePointer();
00577 
00578     if (i && me) {
00579         bool i_connected  = iConnections.containsRef(i);
00580         bool me_connected = i->hasConnectionTo(me);
00581 
00582         if (i_connected || me_connected) {
00583             return true;
00584         } else if (isIConnectionFree() && i->isIConnectionFree()) {
00585 
00586             noticeConnectI(i, i != NULL);
00587             _i->noticeConnectI(me, me != NULL);
00588 
00589             if (!i_connected)
00590                 appendConnectionTo(i);
00591             if (!me_connected)
00592                 _i->appendConnectionTo(me);
00593 
00594             noticeConnectedI(i, i != NULL);
00595             _i->noticeConnectedI(me, me != NULL);
00596 
00597             return true;
00598         } else {
00599             return false;
00600         }
00601     }
00602     return false;
00603 }
00604 
00605 
00606 
00607 template <class thisIF, class cmplIF>
00608 bool InterfaceBase<thisIF, cmplIF>::disconnectI (Interface *__i)
00609 {
00610     cmplClass *_i = dynamic_cast<cmplClass*>(__i);
00611 
00612     // use cache to find pointer in connections list
00613     cmplIF *i = _i ? _i->getThisInterfacePointer() : NULL;
00614 
00615     // The cached me pointer might already point to an destroyed
00616     // object. We must use it only for identifying the entry in
00617     // connections list
00618 
00619     if (i && _i) {
00620         if (me_valid)
00621             noticeDisconnectI(i, _i->isThisInterfacePointerValid());
00622     }
00623 
00624     if (me && _i) {
00625         if (_i->isThisInterfacePointerValid())
00626             _i->noticeDisconnectI(me, me_valid);
00627     }
00628 
00629     if (i && hasConnectionTo(i)) {
00630         removeListener(i);
00631         removeConnectionTo(i);
00632     }
00633 
00634     if (me && i && i->hasConnectionTo(me))
00635         i->removeConnectionTo(me);
00636 
00637     if (me_valid && i && _i)
00638         noticeDisconnectedI(i, _i->isThisInterfacePointerValid());
00639     if (_i && _i->isThisInterfacePointerValid() && me)
00640         _i->noticeDisconnectedI(me, me_valid);
00641 
00642     return true;
00643 }
00644 
00645 
00646 template <class thisIF, class cmplIF>
00647 void InterfaceBase<thisIF, cmplIF>::noticeDisconnectI(cmplInterface *i, bool /*pointer_valid*/)
00648 {
00649     removeListener(i);
00650 }
00651 
00652 
00653 template <class thisIF, class cmplIF>
00654 void InterfaceBase<thisIF, cmplIF>::disconnectAllI()
00655 {
00656     IFList tmp = iConnections;
00657     for (IFIterator it(tmp); it.current(); ++it) {
00658         /* Do not call virtual methods if I'm in the contstructor!
00659            Actually this should be ensured by the compiler generated
00660            code and virtual method tables, but unfortunately some compilers
00661            seem to ignore this in some situations.
00662          */
00663         if (me_valid)
00664             disconnectI(it.current());
00665         else
00666             thisClass::disconnectI(it.current());
00667     }
00668 }
00669 
00670 
00671 
00672 
00673 template <class thisIF, class cmplIF>
00674 bool InterfaceBase<thisIF, cmplIF>::addListener(const cmplInterface *i, QPtrList<cmplInterface> &list)
00675 {
00676     if (iConnections.containsRef(i) && !list.contains(i)) {
00677         list.append(i);
00678         m_FineListeners[i].append(&list);
00679         return true;
00680     } else {
00681         return false;
00682     }
00683 }
00684 
00685 
00686 template <class thisIF, class cmplIF>
00687 void InterfaceBase<thisIF, cmplIF>::removeListener(const cmplInterface *i, QPtrList<cmplInterface> &list)
00688 {
00689     list.remove(i);
00690     if (m_FineListeners.contains(i))
00691         m_FineListeners[i].remove(&list);
00692 }
00693 
00694 
00695 template <class thisIF, class cmplIF>
00696 void InterfaceBase<thisIF, cmplIF>::removeListener(const cmplInterface *i)
00697 {
00698     if (m_FineListeners.contains(i)) {
00699         QPtrList<QPtrList<cmplInterface> >        &list = m_FineListeners[i];
00700         QPtrListIterator<QPtrList<cmplInterface> > it(list);
00701         for (; it.current(); ++it) {
00702             (*it)->remove(i);
00703         }
00704     }
00705     m_FineListeners.remove(i);
00706 }
00707 
00708 
00709 
00710 
00711 
00712 
00713 
00714 #endif
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated on 28 Jan 2011 for kradio by  doxygen 1.6.1