Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

StreamModule.h

00001 #ifndef _STR_StreamModule_H_  // -*- mode: c++ ; c-file-style: "hopper" -*-
00002 
00003 /*
00004  * Copyright (C) 1991-9 Eric M. Hopper <hopper@omnifarious.mn.org>
00005  * 
00006  *     This program is free software; you can redistribute it and/or modify it
00007  *     under the terms of the GNU Lesser General Public License as published
00008  *     by the Free Software Foundation; either version 2 of the License, or
00009  *     (at your option) any later version.
00010  * 
00011  *     This program is distributed in the hope that it will be useful, but
00012  *     WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *     Lesser General Public License for more details.
00015  * 
00016  *     You should have received a copy of the GNU Lesser General Public
00017  *     License along with this program; if not, write to the Free Software
00018  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 
00021 #ifdef __GNUG__
00022 #  pragma interface
00023 #endif
00024 
00025 /* $Header: /home/hopper/src/cvs/C++/StrMod/StrMod/StreamModule.h,v 1.18 2002/08/29 00:58:05 hopper Exp $ */
00026 
00027 // For log information, see ../ChangeLog
00028 
00029 #include <cassert>
00030 #include <cstddef>
00031 
00032 #include <LCore/Protocol.h>
00033 
00034 #ifndef _STR_STR_ClassIdent_H_
00035 #   include <StrMod/STR_ClassIdent.h>
00036 #endif
00037 
00038 #ifndef _STR_StrChunkPtr_H_
00039 #   include <StrMod/StrChunkPtr.h>
00040 #endif
00041 
00042 #define _STR_StreamModule_H_
00043 
00044 namespace strmod {
00045 namespace strmod {
00046 
00047 class StrChunkPtr;
00048 
00049 /** \class StreamModule StreamModule.h StrMod/StreamModule.h
00050  * An abstract base for objects that can be modules in the StreamModule
00051  * framework.
00052  *
00053  * A StreamModule module can be thought of as a processing element in a stream
00054  * of data.  A StreamModule's 'sides' represent ports at which data can enter
00055  * or leave the module.
00056  *
00057  * If you were to think of the UNIX utility 'grep' as a StreamModule, it's 0
00058  * side would be stdin, and it's 1 side would be stdout.  Depending on the
00059  * implementation, it might also have a 2 side, where error messages are sent.
00060  *
00061  * Of course, grep's 'sides' are not bi-directional, they are uni-directional.
00062  * As a rule, a StreamModule's data port (a Plug) is bi-directional.  One way
00063  * to encapsulate a UNIX program designed to run in a pipeline would be to
00064  * make a module that has a 0 side (stdin) a 1 side, with it either passing
00065  * data unchanged in the reverse direction, or refusing to pass data in the
00066  * reverse direction at all.
00067  *
00068  * Another way would be to model it as a single sided StreamModule with it's
00069  * input and output happening on the same plug.  This would sort of violate
00070  * the spirit of the StreamModule system though.  There's currently only one
00071  * module which operates this way, the EchoModule, which merely echoes all of
00072  * its input to its output.  */
00073 
00074 class StreamModule : public lcore::Protocol
00075 {
00076  public:
00077    class Plug;
00078    friend class Plug;
00079    /**
00080     * The Strategy for what to do when a plug is disconnected from another
00081     * plug.
00082     * 
00083     * Probably best used as a Flyweight.
00084     * 
00085     * Not called on module deletion, or if the module thinks it handles its
00086     * plugs disconnecting and there's no room for a Strategy to get involved.
00087     */
00088    class PlugDisconnectStrategy {
00089     public:
00090        /**
00091         * Here so derived classes have a virtual destructor.
00092         */
00093       virtual ~PlugDisconnectStrategy()  { }
00094 
00095       /**
00096        * Called by a module when one of its plugs has been disconnected.
00097        *
00098        * This is not called when a plug is disconnected because the module
00099        * itself is being destructed.
00100        */
00101       virtual void plugDisconnected(StreamModule *m, Plug *p) = 0;
00102    };
00103 
00104    static const STR_ClassIdent identifier;
00105 
00106    //! Abstract class, doesn't do much.
00107    StreamModule() : pdstrategy_(NULL)    { }
00108    //! Prudently virtual in an abstract base class.
00109    virtual ~StreamModule()               { pdstrategy_ = 0; }
00110 
00111    inline virtual int AreYouA(const lcore::ClassIdent &id) const;
00112 
00113    //! Can a plug be created on the given side?
00114    virtual bool canCreate(int side) const = 0;
00115 
00116    /**
00117     * Attempts to create a plug on the given side.
00118     *
00119     * When writing a derived class, your MakePlug function should \b ALWAYS
00120     * call CanCreate first before calling i_MakePlug.
00121     *
00122     * Returns 0 (NULL) on failure.  Only fails if CanCreate would return
00123     * false.
00124     */
00125    inline Plug *makePlug(int side);
00126 
00127    //! Does the module own this plug?
00128    virtual bool ownsPlug(const Plug *plug) const = 0;
00129 
00130    /**
00131     * Please delete this plug.
00132     *
00133     * Modules are supposed to own plugs, so you aren't supposed to delete them
00134     * yourself.
00135     *
00136     * Returns false on failure.  Failure could happen because module does not
00137     * own the plug.
00138     */
00139    virtual bool deletePlug(Plug *plug) = 0;
00140 
00141    /**
00142     * Get the current PlugDisconnectStrategy.
00143     *
00144     * Deleting the object you get this way is a BAD thing unless you set a new
00145     * strategy before you do it.
00146     */
00147    inline PlugDisconnectStrategy *getPDStrategy() const;
00148    /**
00149     * Set the current PlugDisconnectStrategy.
00150     *
00151     * The StreamModule does not assume responsibility for deleting the object
00152     * you pass in.  Of course, if you delete it while the StreamModule has it
00153     * set, that will probably cause bad things to happen.
00154     *
00155     * Setting the strategy to NULL essentially turns it off.  */
00156    inline void setPDStrategy(PlugDisconnectStrategy *pds);
00157 
00158  protected:
00159    virtual const lcore::ClassIdent *i_GetIdent() const { return(&identifier); }
00160 
00161    /**
00162     * Called whenever a plug is disconnected.
00163     *
00164     * <b>This function isn't called when a plug is disconnected as a result of
00165     * its destructor being called.</b>
00166     *
00167     * This is used to provide a hook to do what you think you should do when a
00168     * plug is disconnected.
00169     *
00170     * It calls the pdstrategy_ by default, if there is one.
00171     */
00172    inline virtual void plugDisconnected(Plug *plug);
00173 
00174    /**
00175     * Makes a plug on the given side.
00176     *
00177     * Guaranteed to never be called if canCreate would return false.  Must
00178     * NEVER return 0 (NULL).
00179     */
00180    virtual Plug *i_MakePlug(int side) = 0;
00181 
00182    //! Used to set the readable flag of a plug since their set method is
00183    //! protected.
00184    inline void setReadableFlagFor(Plug *p, bool val);
00185    //! Used to set the writeable flag of a plug since their set method is
00186    //! protected.
00187    inline void setWriteableFlagFor(Plug *p, bool val);
00188 
00189  private:
00190    PlugDisconnectStrategy *pdstrategy_;
00191 };
00192 
00193 /** \class StreamModule::Plug StreamModule.h StrMod/StreamModule.h
00194  * A point of connection between one StreamModule and another.
00195  */
00196 class StreamModule::Plug : public Protocol {
00197  public:
00198    friend class StreamModule;
00199    static const STR_ClassIdent identifier;
00200 
00201    //! A Plug has to have a parent.
00202    inline Plug(StreamModule &parent);
00203    //! Does some tricky things to avoid having the disconnect strategy called.
00204    inline virtual ~Plug();
00205 
00206    inline virtual int AreYouA(const lcore::ClassIdent &cid) const;
00207 
00208    //! Can this plug be read from?
00209    bool isReadable() const   { return(flags_.canread_ && !flags_.isreading_); }
00210    //! Can this plug be written to?
00211    bool isWriteable() const  { return(flags_.canwrite_ && !flags_.iswriting_); }
00212 
00213    /**
00214     * \brief Plug this plug into another.  Can fail if already plugged in.
00215     */
00216    bool plugInto(Plug &other);
00217    //! Unplug this plug from any plugs it may be connected to.
00218    inline void unPlug();
00219    //! \brief Which plug (if any) is this plug plugged into?  Returns NULL if
00220    //! not plugged in.
00221    inline Plug *pluggedInto() const;
00222 
00223    //! Which module owns this plug?
00224    StreamModule &getParent() const            { return(parent_); }
00225 
00226    //! Which 'side' is this plug on?
00227    virtual int side() const = 0;
00228 
00229  protected:
00230    /**
00231     * A struct just so raw bitfields don't have to be in the class.
00232     */
00233    struct Flags {
00234        bool isreading_ : 1; ///< re-entrancy control
00235        bool iswriting_ : 1; ///< re-entrancy control
00236        bool canread_   : 1; ///< Can the plug be read from?
00237        bool canwrite_  : 1; ///< Can the plug be written to?
00238        bool notifyonread_  : 1; ///< Does the plug need to be told if its partner can be read from?
00239        bool notifyonwrite_ : 1; ///< Does the plug need to be told if its partner can be written to?
00240    };
00241 
00242    virtual const lcore::ClassIdent *i_GetIdent() const { return(&identifier); }
00243 
00244    /**
00245     * Set whether this plug is readable or not.
00246     *
00247     * Even if this is set, isReadable can return false because the plug is
00248     * already being read from, and a plug can't be read by two things at once.
00249     */
00250    inline void setReadable(bool val);
00251    /**
00252     * Set whether this plug is writeable or not.
00253     *
00254     * Even if this is set, isWriteable can return false because the plug is
00255     * already being written to, and a plug can't be written to by two things
00256     * at once.
00257     */
00258    inline void setWriteable(bool val);
00259 
00260    //! These are so derived classes have access to this flag.
00261    void setIsReading(bool val)                { flags_.isreading_ = val; }
00262    //! These are so derived classes have access to this flag on other plugs.
00263    static void setIsReading(Plug &othr, bool v)        { othr.setIsReading(v); }
00264    //! These are so derived classes have access to this flag.
00265    void setIsWriting(bool val)                { flags_.iswriting_ = val; }
00266    //! These are so derived classes have access to this flag on other plugs.
00267    static void setIsWriting(Plug &othr, bool v)        { othr.setIsWriting(v); }
00268 
00269    //! This is so derived classes can get read access to the flags of any plug.
00270    inline static const Flags &getFlagsFrom(const Plug &p);
00271 
00272    /**
00273     * Tell a connected plug that this plug's readable state has changed.
00274     *
00275     * This checks the flags first and doesn't notify if the other plug doesn't
00276     * request notification.
00277     */
00278    inline void notifyOtherReadable() const;
00279    /**
00280     * Tell a connected plug that this plug's readable state has changed.
00281     *
00282     * This checks the flags first and doesn't notify if the other plug doesn't
00283     * request notification.
00284     */
00285    inline void notifyOtherWriteable() const;
00286 
00287    /**
00288     * If this plug needs to be notified of the other's readable state.
00289     *
00290     * The only plugs that should really need this are ones for modules where
00291     * the readable or writeable state of certain plugs depends on the readable
00292     * state of the plug that this plug is plugged into.
00293     */
00294    virtual bool needsNotifyReadable() const            { return(false); }
00295    /**
00296     * If this plug needs to be notified of the other's writeable state.
00297     *
00298     * The only plugs that should really need this are ones for modules where
00299     * the readable or writeable state of certain plugs depends on the
00300     * writeable state of the plug that this plug is plugged into.
00301     */
00302    virtual bool needsNotifyWriteable() const           { return(false); }
00303 
00304    /**
00305     * The 'other plug's readable state changed'  notification function.
00306     *
00307     * Called when plug this plug is plugged into changes whether or not it is
00308     * readable.
00309     */
00310    virtual void otherIsReadable()                      { }
00311    /**
00312     * The 'other plug's writeable state changed' notification function.
00313     *
00314     * Called when plug this plug is plugged into changes whether or not it is
00315     * writeable.
00316     */
00317    virtual void otherIsWriteable()                     { }
00318 
00319    /** @name Read and Write
00320     * Actually read or write to a StreamModule::Plug.
00321     *
00322     * These functions have undefined behavior when the appropriate isReadable
00323     * or isWriteable function returns false.
00324     *
00325     * Also, to avoid re-entrancy problems, the flag_.isreading_ or
00326     * flag_.iswriting_ flag should be set before actually doing the read or
00327     * write.  This prevents the read or write from causing the function to be
00328     * called again.
00329     *
00330     * This is of particular importance for modules like EchoModule.  If you
00331     * have two EchoModule objects with a buffering module of some sort between
00332     * then, an infinitely recurisve loop could easily be formed if a read or
00333     * write function were called without the appropriate re-entrancy control
00334     * flag being set.
00335     *
00336     * As it is, you will get an infinite loop, but it will
00337     * not be recursive, so the program will not crash.  (You might have wanted
00338     * the infinite loop for some reason.)
00339     */
00340    //@{
00341    /**
00342     * \brief Read this this plug.  Behavior is undefined if this plug not
00343     * readable.
00344     *
00345     * When writing your own read function that calls this one, you are
00346     * responsible for making sure the the plug is readable before calling this
00347     * function.
00348     *
00349     * These functions do not set the isreading or iswriting
00350     * flags.  You'll need to write wrapper functions that do this.  The
00351     * pushLoop and pullLoop functions already set the isreading and iswriting
00352     * flags.
00353     */
00354    virtual const StrChunkPtr i_Read() = 0;
00355    /**
00356     * \brief Write to this plug.  Behavior is undefined if this plug not
00357     * writeable.
00358     *
00359     * When writing your own write function that calls this one, you are
00360     * responsible for making sure the the plug is writeable before calling
00361     * this function.
00362     * 
00363     * These functions do not set the isreading or iswriting flags.  You'll
00364     * need to write wrapper functions that do this.  The pushLoop and pullLoop
00365     * functions already set the isreading and iswriting flags.
00366     */
00367    virtual void i_Write(const StrChunkPtr &ptr) = 0;
00368    //@}
00369 
00370    //! This is so derived classes can call write on the connected plug.
00371    void writeOther(const StrChunkPtr &ptr) const   { other_->i_Write(ptr); }
00372    //! This is so derived classes can call read on the connected plug.
00373    const StrChunkPtr readOther() const             { return(other_->i_Read()); }
00374 
00375    /**
00376     * Read as much as possible from this plug and write to the connected plug.
00377     */
00378    void pushLoop();
00379    /**
00380     * Read as much as possible from the connected plug and write to this plug.
00381     */
00382    void pullLoop();
00383 
00384  private:
00385    Flags flags_;  ///< See struct Flags.
00386    Plug *other_;  ///< The plug I'm talking to (if any).
00387    StreamModule &parent_;  ///< What StreamModule I came from.
00388 };
00389 
00390 //-----------------------------inline functions--------------------------------
00391 
00392 inline int StreamModule::AreYouA(const lcore::ClassIdent &id) const
00393 {
00394    return((identifier == id) || Protocol::AreYouA(id));
00395 }
00396 
00397 inline StreamModule::Plug *StreamModule::makePlug(int side)
00398 {
00399    if (canCreate(side)) {
00400       Plug *plug = i_MakePlug(side);
00401 
00402       assert(plug != 0);
00403       return(plug);
00404    } else {
00405       return(0);
00406    }
00407 }
00408 
00409 inline StreamModule::PlugDisconnectStrategy *StreamModule::getPDStrategy() const
00410 {
00411    return(pdstrategy_);
00412 }
00413 
00414 inline void StreamModule::setPDStrategy(PlugDisconnectStrategy *pds)
00415 {
00416    pdstrategy_ = pds;
00417 }
00418 
00419 inline void StreamModule::plugDisconnected(Plug *plug)
00420 {
00421    if (pdstrategy_ != NULL)
00422    {
00423       pdstrategy_->plugDisconnected(this, plug);
00424    }
00425 }
00426 
00427 inline void StreamModule::setReadableFlagFor(Plug *p, bool val)
00428 {
00429    p->setReadable(val);
00430 }
00431 
00432 inline void StreamModule::setWriteableFlagFor(Plug *p, bool val)
00433 {
00434    p->setWriteable(val);
00435 }
00436 
00437 //---
00438 
00439 inline StreamModule::Plug::Plug(StreamModule &parent)
00440      : other_(NULL),
00441        parent_(parent)
00442 {
00443    flags_.isreading_ = flags_.iswriting_ = flags_.canread_ = flags_.canwrite_
00444       = flags_.notifyonread_ = flags_.notifyonwrite_ = false;
00445 }
00446 
00447 inline StreamModule::Plug::~Plug()
00448 {
00449    if (other_ != NULL)
00450    {
00451       Plug *temp = other_;
00452 
00453       // This prevents the unPlug routine from gaining control.  It will think
00454       // this plug is already unplugged.
00455       other_ = NULL;
00456       flags_.notifyonread_ = flags_.notifyonwrite_ = false;
00457       temp->unPlug();
00458       // Note that parent->plugDisconnected() isn't called here.  This is to
00459       // avoid calling a partially destructed parent, and various similar
00460       // kinds of nastiness.
00461    }
00462 }
00463 
00464 inline int StreamModule::Plug::AreYouA(const lcore::ClassIdent &id) const
00465 {
00466    return((identifier == id) || Protocol::AreYouA(id));
00467 }
00468 
00469 inline void StreamModule::Plug::unPlug()
00470 {
00471    // Only need to unplug if we're plugged in.
00472    if (other_ != NULL)
00473    {
00474       Plug *temp = other_;
00475 
00476       // This prevents infinite recursion from happening when the other plug
00477       // tells us to unplug.  We'll think we're already unplugged.
00478       other_ = NULL;
00479       flags_.notifyonread_ = flags_.notifyonwrite_ = false;
00480       temp->unPlug();
00481       getParent().plugDisconnected(this);
00482    }
00483 }
00484 
00485 inline StreamModule::Plug *StreamModule::Plug::pluggedInto() const
00486 {
00487    return(other_);
00488 }
00489 
00490 inline void StreamModule::Plug::setReadable(bool val)
00491 {
00492    bool oldflag = flags_.canread_;
00493 
00494    flags_.canread_ = val;
00495 
00496    if (isReadable() && pluggedInto() != NULL)
00497    {
00498       pushLoop();
00499    }
00500    if (flags_.canread_ != oldflag)
00501    {
00502       notifyOtherReadable();
00503    }
00504 }
00505 
00506 inline void StreamModule::Plug::setWriteable(bool val)
00507 {
00508    bool oldflag = flags_.canwrite_;
00509 
00510    flags_.canwrite_ = val;
00511 
00512    if (isWriteable() && pluggedInto() != NULL)
00513    {
00514       pullLoop();
00515    }
00516    if (flags_.canwrite_ != oldflag)
00517    {
00518       notifyOtherWriteable();
00519    }
00520 }
00521 
00522 inline const StreamModule::Plug::Flags &
00523 StreamModule::Plug::getFlagsFrom(const Plug &p)
00524 {
00525    return(p.flags_);
00526 }
00527 
00528 inline void StreamModule::Plug::notifyOtherReadable() const
00529 {
00530    if (flags_.notifyonread_)
00531    {
00532       assert(other_ != NULL);
00533       other_->otherIsReadable();
00534    }
00535 }
00536 
00537 inline void StreamModule::Plug::notifyOtherWriteable() const
00538 {
00539    if (flags_.notifyonwrite_)
00540    {
00541       assert(other_ != NULL);
00542       other_->otherIsWriteable();
00543    }
00544 }
00545 
00546 }  // namespace strmod
00547 }  // namespace strmod
00548 
00549 #endif

Generated on Wed Jan 29 00:32:44 2003 for libNet by doxygen1.3-rc1