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
1.3-rc1