Packets: Fix VariantParser invalid parser access bug
[senf.git] / PPI / Connectors.hh
index c915c06..07316b6 100644 (file)
@@ -1,6 +1,8 @@
-// Copyright (C) 2007 
-// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
-// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+// $Id$
+//
+// Copyright (C) 2007
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
 //     Stefan Bund <g0dil@berlios.de>
 //
 // This program is free software; you can redistribute it and/or modify
 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 /** \file
-    \brief Conenctors public header */
-
-/** \defgroup connectors Connector classes
-
-    A connector has two independent properties
-    \li it may be \e active or \e passive
-    \li it may be an \e input or an \e output
-    
-    \e Active connectors are activated from within the module, \e passive connectors are signaled by
-    the external framework. \e Input modules receive packets, \e output modules send packets.
-
-    All passive connectors call some onRequest callback whenever I/O needs to be performed. All
-    input modules possess a packet queue.
+    \brief Connectors public header */
 
-    We therefore have 4 connector types: senf::ppi::ActiveInput, senf::ppi::ActiveOutput,
-    senf::ppi::PassiveInput and senf::ppi::PassiveOutput.
- */
-
-#ifndef HH_Conenctors_
-#define HH_Conenctors_ 1
+#ifndef HH_SENF_PPI_Connectors_
+#define HH_SENF_PPI_Connectors_ 1
 
 // Custom includes
+#include <deque>
 #include <boost/utility.hpp>
-
-//#include "Conenctors.mpp"
+#include <boost/scoped_ptr.hpp>
+#include "../Utils/safe_bool.hh"
+#include "../Utils/Exception.hh"
+#include "../Packets/Packets.hh"
+#include "predecl.hh"
+#include "detail/Callback.hh"
+#include "Queueing.hh"
+#include "ModuleManager.hh"
+
+//#include "Connectors.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
 
 namespace senf {
 namespace ppi {
 namespace connector {
 
-    /** \brief Connector baseclass
+    /** \namespace senf::ppi::connector
+        \brief Connector classes
+
+        A connector has three independent properties
+        \li it may be \e active or \e passive
+        \li it may be an \e input or an \e output
+        \li it has an (optional) packet type
+
+        \e Active connectors are activated from within the module, \e passive connectors are
+        signaled by the external framework. \e Input connectors receive packets, \e output
+        connectors send packets.
+
+        All passive connectors call some onRequest callback whenever I/O needs to be performed. All
+        input connectors possess a packet queue.
+
+        We therefore have 4 connector types each of which is parameterized by the type of packet
+        traversing the connector:
+        \li senf::ppi::connector::ActiveInput
+        \li senf::ppi::connector::ActiveOutput 
+        \li senf::ppi::connector::PassiveInput 
+        \li senf::ppi::connector::PassiveOutput.
+
+        Connectors are declared as module data members and are then externally connected to other
+        modules.
+
+        The connectors each take an optional template argument. If this argument is specified, it
+        must be the type of packet expected or sent on this connector. If it is not specified,
+        packets will be passed using the generic Packet handle.
+
+        \code
+        class IpFilter : public senf::ppi::module::Module
+        {
+            SENF_PPI_MODULE(SomeModule);
+
+        public:
+            senf::ppi::connector::ActiveInput<senf::EthernetPacket> input;
+            senf::ppi::connector::PassiveOutput<senf::IpPacket> output;
+
+            IpFilter() {
+                route(input, output);
+                input.onRequest(&IpFilter::onRequest);
+            }
+
+        private:
+            void onRequest() {
+                // 'input()' will return a senf::EthernetPacket packet handle
+                try { output( input().find<senf::IpPacket>() ); }
+                catch (senf::InvalidPacketChainException & ex) { ; }
+            }
+        };
+        \endcode
+
+        \see
+            senf::ppi::module::Module \n
+            senf::ppi::connect() \n
+            \ref ppi_connectors
+     */
+
+    /** \brief Incompatible connectors connected
+
+        This exception is thrown, when two incompatible connectors are connected. This happens if
+        both connectors of a senf::ppi::connect() statement declare a packet type (the connector
+        template argument) but they don't declare the same packet type.
+
+        You need to ensure, that both connectors use the same packet type.
+
+        \see senf::ppi::connect()
+     */
+    struct IncompatibleConnectorsException : public senf::Exception
+    { IncompatibleConnectorsException() : senf::Exception("Incompatible connectors") {} };
+
+    /** \brief Connector base-class
 
         This connector provides access to the generic connector facilities. This includes the
         connection management (access to the connected peer) and the containment management (access
         to the containing module)
      */
     class Connector
-        : boost::noncopyable
+        : ModuleManager::Initializable, boost::noncopyable
     {
     public:
-        Connector & peer();             ///< Get peer connected to this connector
-        Module & module();              ///< Get this connectors containing module
+        Connector & peer() const;       ///< Get peer connected to this connector
+        module::Module & module() const; ///< Get this connectors containing module
+
+        bool connected() const;         ///< \c true, if connector connected, \c false otherwise
+
+        void disconnect();              ///< Disconnect connector from peer
 
     protected:
-        // here to protect
         Connector();
-        ~Connector();
+        virtual ~Connector();
+
+        void connect(Connector & target);
+        
+    private:
+        virtual std::type_info const & packetTypeID();
+
+        void setModule(module::Module & module);
+
+        Connector * peer_;
+        module::Module * module_;
+
+        friend class module::Module;
     };
 
-    /** \brief Passive connector baseclass
+    /** \brief Passive connector base-class
 
         A passive connector is a connector which is activated externally whenever an I/O request
         occurs. Passive connectors are the origin of throttling notifications. Depending on the type
         of connector (output or input) the respective throttling is called forward or backward
         throttling.
 
-        Passive connectors always handle two throttling states: 
-        
-        \li The \e native throttling state is set manually by the module. It is the throttling state
+        Passive connectors always handle two throttling states:
+
+        - The \e native throttling state is set manually by the module. It is the throttling state
             originating in the current module
-        \li The \e forwarded throttling state is the state as it is received by throttling
+        - The \e forwarded throttling state is the state as it is received by throttling
             notifications
 
         The accumulative throttling state is generated by combining all sub-states.
      */
-    class PassiveConnector 
+    class PassiveConnector
         : public virtual Connector
     {
     public:
@@ -98,29 +179,54 @@ namespace connector {
                                              is a pointer-to-member to a member of the class which
                                              holds this input. In the second case, the pointer will
                                              automatically be bound to the containing instance.
-                                             
+
                                              \param[in] handler Handler to call, whenever an I/O
                                                  operation is to be performed. */
 
-        
-        bool throttled();               ///< Get accumulative throttling state
-        bool nativeThrottled();         ///< Get native throttling state
+
+        bool throttled() const;         ///< Get accumulative throttling state
+        bool nativeThrottled() const;   ///< Get native throttling state
 
         void throttle();                ///< Set native throttling
         void unthrottle();              ///< Revoke native throttling
-        
-        void notifyThrottle();          ///< Forward a throttling notification to this connector
-        void notifyUnthrottle();        ///< Forward an unthrottling notification to this connector
 
-        ActiveConnector & peer();
+        ActiveConnector & peer() const;
 
     protected:
-        // here to protect
         PassiveConnector();
-        ~PassiveConnector();
+
+        void emit();
+
+    private:
+        virtual void v_init();
+
+        // Called by the routing to change the remote throttling state
+        void notifyThrottle();          ///< Forward a throttle notification to this connector
+        void notifyUnthrottle();        ///< Forward an unthrottle notification to this connector
+
+        // Internal members to emit throttling notifications
+        void emitThrottle();
+        void emitUnthrottle();
+
+        // Called after unthrottling the connector
+        virtual void v_unthrottleEvent();
+
+        // called by ForwardingRoute to register a new route
+        void registerRoute(ForwardingRoute & route);
+
+        typedef ppi::detail::Callback<>::type Callback;
+        Callback callback_;
+
+        bool remoteThrottled_;
+        bool nativeThrottled_;
+
+        typedef std::vector<ForwardingRoute*> Routes;
+        Routes routes_;
+
+        friend class senf::ppi::ForwardingRoute;
     };
 
-    /** \brief Active connector baseclass
+    /** \brief Active connector base-class
 
         An active connector is a connector which emits I/O requests. Active connectors receive
         throttling notifications. Depending on the type of connector (input or output) the
@@ -130,12 +236,13 @@ namespace connector {
         notifications. These notifications should then either be processed by the module or be
         forwarded to other connectors.
      */
-    class ActiveConnector 
+    class ActiveConnector
         : public virtual Connector
     {
+        typedef ppi::detail::Callback<>::type Callback;
     public:
         template <class Handler>
-        void onThrottle(Handler handle); ///< Register throttle notification handler
+        void onThrottle(Handler handler); ///< Register throttle notification handler
                                         /**< The handler register here will be called, whenever a
                                              throttle notification comes in. The \a handler argument
                                              is either an arbitrary callable object or it is a
@@ -143,11 +250,12 @@ namespace connector {
                                              this input. In the second case, the pointer will
                                              automatically be bound to the containing instance.
 
-                                             \param[in] handle Handler to call on throttle
+                                             \param[in] handler Handler to call on throttle
                                                  notifications. */
+        void onThrottle();              ///< Clear throttle notification handler
 
         template <class Handler>
-        void onUnthrottle(Handler handle); ///< Register unthrottle notification handler
+        void onUnthrottle(Handler handler); ///< Register unthrottle notification handler
                                         /**< The handler register here will be called, whenever an
                                              unthrottle notification comes in. The \a handler
                                              argument is either an arbitrary callable object or it
@@ -155,18 +263,40 @@ namespace connector {
                                              holds this input. In the second case, the pointer will
                                              automatically be bound to the containing instance.
 
-                                             \param[in] handle Handler to call on unthrottle
+                                             \param[in] handler Handler to call on unthrottle
                                                  notifications. */
+        void onUnthrottle();            ///< Clear unthrottle notification handler
+
+        bool throttled() const;         ///< \c true, if peer() is throttled
 
-        PassiveConnector & peer();
+        PassiveConnector & peer() const;
 
     protected:
-        // here to protect
-        PassiveConnector();
-        ~PassiveConnector();
+        ActiveConnector();
+
+    private:
+        virtual void v_init();
+
+        // called by the peer() to forward throttling notifications
+        void notifyThrottle();
+        void notifyUnthrottle();
+
+        // called by ForwardingRoute to register a new route
+        void registerRoute(ForwardingRoute & route);
+
+        Callback throttleCallback_;
+        Callback unthrottleCallback_;
+
+        typedef std::vector<ForwardingRoute*> NotifyRoutes;
+        NotifyRoutes notifyRoutes_;
+
+        bool throttled_;        
+
+        friend class senf::ppi::ForwardingRoute;
+        friend class PassiveConnector;
     };
 
-    /** \brief Input connector baseclass
+    /** \brief Input connector base-class
 
         An input connector receives packets. It may be either an ActiveConnector or a
         PassiveConnector. An input connector contains a packet queue. This queue enables processing
@@ -183,17 +313,19 @@ namespace connector {
                 insertion/removal properties on both ends.
 
             So probably we will use a deque. I'd like a container which keeps iterators intact on
-            isertion/deletion but I believe that list is just to expensive since every packet will
+            insertion/deletion but I believe that list is just to expensive since every packet will
             be added to the queue before it can be processed.
      */
-    class InputConnector 
+    class InputConnector
         : public virtual Connector
     {
+        typedef std::deque<Packet> Queue;
     public:
-        typedef unspecified queue_iterator; ///< Iterator type of the embedded queue
-        typedef unspecified size_type;  ///< Unsigned type representing the number of queue elements
+        typedef Queue::const_iterator queue_iterator; ///< Iterator type of the embedded queue
+        typedef Queue::size_type size_type; ///< Unsigned type for counting queue elements
+
 
-        Packet::ptr operator();         ///< Get a packet
+        Packet operator()();            ///< Get a packet
                                         /**< This member is the primary method to access received
                                              data. On passive connectors, this operator will just
                                              dequeue a packet from the packet queue. If the
@@ -202,123 +334,274 @@ namespace connector {
                                              request cannot be fulfilled, this is considered to be a
                                              logic error in the module implementation and an
                                              exception is raised. */
-        operator unspecified_boolean_type (); ///< Check packet availability
-                                        /**< Using any input connector in a boolean context will
-                                             check, whether an input request can be fulfilled. This
-                                             is always possible if the queue is non-empty. If the
-                                             input is active, it also returns when the connected
-                                             passive output is not throttled so new packets can be
-                                             requested. 
 
-                                             Calling the operator() member is an error if this test
-                                             returns \c false
+        Packet read();                  ///< Alias for operator()()
 
-                                             \returns \c true if operator() can be called, \c false
-                                                 otherwise */
-        operator ! ();                  ///< Check packet availability
-                                        /**< Inverse of the boolean conversion operator
-                                             \returns \c false if operator() can be called, \c true
-                                                 otherwise */
+        OutputConnector & peer() const;
 
-        OutputConnector & peer();
+        queue_iterator begin() const;   ///< Access queue begin (head)
+        queue_iterator end() const;     ///< Access queue past-the-end (tail)
+        Packet peek() const;            ///< Return head element from the queue
 
-        queue_iterator begin();         ///< Access queue begin (head)
-        queue_iterator end();           ///< Access queue past-the-end (tail)
-        Packet::ptr head();             ///< Return head element from the queue
-
-        size_type queueSize();          ///< Return number of elements in the queue
-        bool empty();                   ///< Return queueSize() == 0
+        size_type queueSize() const;    ///< Return number of elements in the queue
+        bool empty() const;             ///< Return queueSize() == 0
 
     protected:
-        // here to protect
-        PassiveConnector();
-        ~PassiveConnector();
+        InputConnector();
+
+    private:
+        void enqueue(Packet p);
+
+        virtual void v_requestEvent();
+        virtual void v_enqueueEvent();
+        virtual void v_dequeueEvent();
+
+        Queue queue_;
+
+        friend class OutputConnector;
     };
-    
-    /** \brief Output connector baseclass
-        
+
+    /** \brief Output connector base-class
+
         An output connector sends out packets. It may be either an ActiveConnector or a
         PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
         the queueing of the connected input.
      */
-    class OutputConnector 
+    class OutputConnector
         : public virtual Connector
     {
     public:
-        void operator(Packet::ptr);     ///< Send out a packet
+        void operator()(Packet p);      ///< Send out a packet
+
+        void write(Packet p);           ///< Alias for operator()(Packet p)
 
-        InputConnector & peer();
+        InputConnector & peer() const;
 
     protected:
-        // here to protect
-        PassiveConnector();
-        ~PassiveConnector();
+        OutputConnector();
     };
-    
-    ///@{
-    ///\addtogroup connectors
 
     /** \brief Combination of PassiveConnector and InputConnector
 
-        In addition to the native and the forwarded throttling state, the PassiveInput manages a
-        queue throttling state. This state is automatically managed by a queueing discipline. The
-        standard queueing discipline is ThresholdQueueing, which throttles the connection whenever
-        the queue length reaches the high threshold and unthrottles the connection when the queue
-        reaches the low threshold. The default queueing discipline is
+        The GenericPassiveInput automatically controls the connectors throttling state using a
+        queueing discipline. The standard queueing discipline is ThresholdQueueing, which throttles
+        the connection whenever the queue length reaches the high threshold and unthrottles the
+        connection when the queue reaches the low threshold. The default queueing discipline is
         <tt>ThresholdQueueing(1,0)</tt> which will throttle the input whenever the queue is
         non-empty.
      */
-    class PassiveInput 
-        : public PassiveConnector, public InputConnector
+    class GenericPassiveInput
+        : public PassiveConnector, public InputConnector,
+          public safe_bool<GenericPassiveInput>
     {
     public:
-        ActiveOutput & peer();
+        GenericActiveOutput & peer() const;
+
+        bool boolean_test() const;      ///< \c true, if ! empty()
 
-        template <class QueueingDiscipline>
-        void qdisc(QueueingDiscipline const & disc); ///< Change the queueing discipline
+        template <class QDisc>
+        void qdisc(QDisc const & disc); ///< Change the queueing discipline
                                         /**< The queueing discipline is a class which provides the
                                              QueueingDiscipline interface.
-                                             
+
                                              \param[in] disc New queueing discipline */
+
+    protected:
+        GenericPassiveInput();
+
+    private:
+        void v_enqueueEvent();
+        void v_dequeueEvent();
+        void v_unthrottleEvent();
+
+        boost::scoped_ptr<QueueingDiscipline> qdisc_;
     };
 
     /** \brief Combination of PassiveConnector and OutputConnector
      */
-    class PassiveOutput
-        : public PassiveConnector, public OutputConnector
+    class GenericPassiveOutput
+        : public PassiveConnector, public OutputConnector,
+          public safe_bool<GenericPassiveOutput>
     {
     public:
-        ActiveInput & peer();
+        GenericActiveInput & peer() const;
+
+        bool boolean_test() const;      ///< Always \c true
+
+        void connect(GenericActiveInput & target); ///< Internal: Use senf::ppi::connect() instead
+
+        friend class GenericActiveInput;
+
+    protected:
+        GenericPassiveOutput();
+
     };
 
     /** \brief Combination of ActiveConnector and InputConnector
      */
-    class ActiveInput
-        : public ActiveConnector, public InputConnector
+    class GenericActiveInput
+        : public ActiveConnector, public InputConnector,
+          public safe_bool<GenericActiveInput>
     {
     public:
-        PassiveOutput & peer();
+        GenericPassiveOutput & peer() const;
+
+        bool boolean_test() const;      ///< \c true, if ! empty() or ! throttled()
 
         void request();                 ///< request more packets without dequeuing any packet
+
+    protected:
+        GenericActiveInput();
+
+    private:
+        void v_requestEvent();
     };
 
     /** \brief Combination of ActiveConnector and OutputConnector
      */
-    class ActiveOutput
-        : public ActiveConnector, public OutputConnector
+    class GenericActiveOutput
+        : public ActiveConnector, public OutputConnector,
+          public safe_bool<GenericActiveOutput>
     {
     public:
-        ActiveInput & peer();
+        GenericPassiveInput & peer() const;
+
+        bool boolean_test() const;      ///< \c true if peer() is ! throttled()
+
+        void connect(GenericPassiveInput & target); ///< Internal: Use senf::ppi::connect() instead
+
+    protected:
+        GenericActiveOutput();
+
     };
 
-    ///@}
+#ifndef DOXYGEN
+
+#   define TypedConnector_Input read
+#   define TypedConnector_Output write
+#   define TypedConnector(pType, dir)                                                             \
+        template <class PacketType>                                                               \
+        class pType ## dir                                                                        \
+            : public Generic ## pType ## dir,                                                     \
+              private detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>        \
+        {                                                                                         \
+            typedef detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType> mixin;   \
+        public:                                                                                   \
+            using mixin::operator();                                                              \
+            using mixin::TypedConnector_ ## dir ;                                                 \
+        private:                                                                                  \
+            virtual std::type_info const & packetTypeID()                                         \
+                { return typeid(typename PacketType::type); }                                     \
+            friend class detail::Typed ## dir ## Mixin<pType ## dir <PacketType>, PacketType>;    \
+        };                                                                                        \
+        template <>                                                                               \
+        class pType ## dir <Packet> : public Generic ## pType ## dir                              \
+        {}
+
+    TypedConnector( Passive, Input  );
+    TypedConnector( Passive, Output );
+    TypedConnector( Active,  Input  );
+    TypedConnector( Active,  Output );
+
+#   undef TypedConnector
+#   undef TypedConnector_Input
+#   undef TypedConnector_Output
+
+#else
+
+    /** \brief Connector actively reading packets
+        
+        \tparam PacketType Type of packet to read. Defaults to senf::Packet
+
+        The %ActiveInput %connector template reads data actively from a connected %module. This
+        class is completely implemented via it's base-class, GenericActiveInput, the only 
+        difference is that read packets are returned as \a PacketType instead of generic 
+        senf::Packet references.
+
+        \see GenericActiveInput \n
+            senf::ppi::connector
+     */
+    template <class PacketType=Packet>
+    class ActiveInput : public GenericActiveInput
+    {
+    public:
+        PacketType operator()();        ///< Read packet
+                                        /**< \throws std::bad_cast, if the %connector receives a
+                                             Packet which is not of type \a PacketType.
+                                             \returns newly read packet reference. */
+        PacketType read();              ///< Alias for operator()
+    };
+
+    /** \brief Connector passively receiving packets
+
+        \tparam PacketType Type of packet to read. Defaults to senf::Packet
+
+        The %PassiveInput %connector template receives packets sent to it from a connected
+        %module. This class is completely implemented via it's base-class, GenericPassiveInput, 
+        the only difference is that read packets are returned as \a PacketType instead of generic
+        senf::Packet references.
+
+        \see GenericPassiveInput \n
+            senf::ppi::connector
+     */
+    template <class PacketType=Packet>
+    class PassiveInput : public GenericPassiveInput
+    {
+    public:
+        PacketType operator()();        ///< Read packet
+                                        /**< \throws std::bad_cast, if the %connector receives a
+                                             Packet which is not of type \a PacketType.
+                                             \returns newly read packet reference. */
+        PacketType read();              ///< Alias for operator()
+    };
+
+    /** \brief Connector actively sending packets
+
+        \tparam PacketType Type of packet to send. Defaults to senf::Packet
+
+        The %ActiveOutput %connector template sends data actively to a connected %module. This 
+        class is completely implemented via it's base-class, GenericActiveOutput, the only
+        difference is that it only sends packets of type \a PacketType.
+
+        \see GenericActiveOutput \n
+            senf::ppi::connector
+     */
+    template <class PacketType=Packet>
+    class ActiveOutput : public GenericActiveOutput
+    {
+    public:
+        operator()(PacketType packet);  ///< Send out a packet
+        void write(PacketType packet);  ///< Alias for operator()
+    };
+
+    /** \brief Connector passively providing packets
+
+        \tparam PacketType Type of packet to send. Defaults to senf::Packet
+
+        The %PassiveOutput %connector template provides data passively to a connected %module 
+        whenever signaled. This class is completely implemented via it's base-class, 
+        GenericPassiveOutput, the only difference is that it only sends packets of type 
+        \a PacketType.
+
+        \see GenericPassiveOutput \n
+            senf::ppi::connector
+     */
+    template <class PacketType=Packet>
+    class PassiveOutput : public GenericPassiveOutput
+    {
+    public:
+        operator()(PacketType packet);  ///< Send out a packet
+        void write(PacketType packet);  ///< Alias for operator()
+    };
+
+#endif
 
 }}}
 
 ///////////////////////////////hh.e////////////////////////////////////////
-//#include "Conenctors.cci"
-//#include "Conenctors.ct"
-//#include "Conenctors.cti"
+#include "Connectors.cci"
+//#include "Connectors.ct"
+#include "Connectors.cti"
 #endif
 
 \f