Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

MMAccessor.h

Go to the documentation of this file.
00001 #ifndef INCLUDED_MMAccessor_h_
00002 #define INCLUDED_MMAccessor_h_
00003 
00004 #include "MotionManager.h"
00005 
00006 //! This class allows convenient ways of accessing a motion command
00007 /*! Since MotionCommands must be checked out of the motion manager and then checked back
00008  *  in when they are done, this is a common source of errors, leading to deadlock.  This class
00009  *  will check the motion out when it's created, and check it back in when it goes out of scope\n
00010  *  It supports recursive checkin/checkouts. \n
00011  *  Uses global ::motman
00012  *
00013  *  So, instead of doing things like this:
00014  *  @code
00015  *  YourMotionCommand* ymc = dynamic_cast<YourMotionCommand*>(motman->checkoutMotion(your_mc_id));
00016  *  //do 'stuff' with ymc, e.g.: ymc->rollOver();
00017  *  motman->checkinMotion(your_mc_id);
00018  *  @endcode
00019  *  ...which can be error prone in many regards - if 'stuff' returns without checking in, or you
00020  *  forget to check in, or you get lazy and leave it checked out longer than you should, which can
00021  *  cause general performance issues (or worse, deadlock)
00022  *  Using MMAccessor makes it much easier to solve these problems, and is easier to code:
00023  *  @code
00024  *  MMAccessor<YourMotionCommand> mma(your_mc_id);
00025  *  //do 'stuff' with mma, e.g.: mma->rollOver();
00026  *  @endcode
00027  *  We can call a return at any point and the motion command will automatically be checked in, and
00028  *  since C++ guarrantees that the destructor of mma will be called, we don't have to worry about
00029  *  forgetting about it.  We can limit the scope by placing {}'s around the segment in question:
00030  *  @code
00031  *  //pre-stuff
00032  *  {
00033  *    MMAccessor<YourMotionCommand> mma(your_mc_id);
00034  *    //do 'stuff' with mma, e.g.: mma->rollOver();
00035  *  }
00036  *  //post-stuff - has no knowledge of mma, out of its scope
00037  *  @endcode
00038  *  And, for those astute enough to notice that the theoretical @a rollOver() function is called on
00039  *  MMAccessor when it's actually a member of YourMotionCommand, this is because MMAccessor behaves as a
00040  *  'smart pointer', which overloads operator->() so it is fairly transparent to use.
00041  *
00042  *  In some cases, you may wish to access a prunable motion, but may be unsure of whether the
00043  *  motion is still alive.  If it has been pruned, the MC_ID is no longer valid, and will not provide
00044  *  access to the motion.  Worse, however, is that enough new motions have been created that the
00045  *  ID has been recycled and now refers to another, different, motion.
00046  *
00047  *  The solution to this requires two steps.  First, you must retain the SharedObject you used to
00048  *  initially create the motion.  This is required because if the MotionManager prunes the motion,
00049  *  it will dereference the memory region, and if there are no other references to the region, it
00050  *  will be deallocated, destroying the data.  Second, you pass this SharedObject to the MMAccessor
00051  *  constructor as shown:
00052  *  @code
00053  *  SharedObject<YourMC> yourmc;
00054  *  // ... stuff ... if yourmc was added to MotionManager, it may or may not still be active
00055  *  MMAccessor<YourMC> your_acc(*yourmc); // doesn't matter!
00056  *  // your_acc now provides no-op access if not in MotionManager, checks it out if it is
00057  *  @endcode
00058  *  This guarantees safe access regardless to the current status of the motion.  (Note that you can
00059  *  also just listen for the (EventBase::motmanEGID, MC_ID, EventBase::deactivateETID) event
00060  *  to be notified when a motion is pruned... however, unless you still have a reference to
00061  *  the SharedObject, you won't be able to access/reuse the motion after it was pruned)
00062  *
00063  *  MMAccessor is a small class, you may consider passing it around instead of a MotionManager::MC_ID
00064  *  if appropriate.  (Would be appropriate to avoid multiple checkin/outs in a row from different
00065  *  functions, but not as appropriate for storage and reuse of the same MMAccessor.)
00066  */
00067 template<class MC_t>
00068 class MMAccessor {
00069  public:
00070 
00071   //! constructor, checks out by default
00072   /*! @param id the motion command to check out
00073    *  @param ckout if true (default) will checkout upon creation.  otherwise it just gets current address (so you can peek at member fields, which should be safe) */
00074   MMAccessor(MotionManager::MC_ID id,bool ckout=true) : mc_id(id), checkOutCnt(0), mcptr(NULL) {
00075     if(ckout)
00076       checkout();
00077     else
00078       mcptr=static_cast<MC_t*>(motman->peekMotion(id));
00079   }
00080 
00081   //! constructor, allows objects to provide uniform access to MotionCommands, regardless of whether they are currently in the MotionManager
00082   /*! if ckout is true (default parameter), will attempt to check out the motion if the motion reports it has a valid ID */
00083   MMAccessor(MC_t & ptr, bool ckout=true) : mc_id(ptr.getID()), checkOutCnt(0), mcptr(&ptr) {
00084     if(ckout && mc_id!=MotionManager::invalid_MC_ID)
00085       checkout();
00086   }
00087 
00088   //! copy constructor - will reference the same mc_id - checkin/checkouts are independent between this and @a a; however, if @a a is checked out, @c this will check itself out as well
00089   /*! If the original was checked out, this will checkout as well (so #checkOutCnt will be 1) */
00090   MMAccessor(const MMAccessor& a) : mc_id(a.mc_id), checkOutCnt(0), mcptr(a.mcptr) {
00091     if(a.checkOutCnt>0)
00092       checkout();
00093   }
00094 
00095   //! destructor, checks in if needed
00096   ~MMAccessor() {
00097     while(checkOutCnt>0)
00098       checkin();
00099   }
00100 
00101   //! allows assignment of MMAccessor's, similar to the copy constructor - the two MMAccessor's will control the same MotionCommand
00102   /*! If the original was checked out, this will checkout as well (so #checkOutCnt will be 1) */
00103   MMAccessor<MC_t> operator=(const MMAccessor<MC_t>& a) {
00104     mc_id=a.mc_id;
00105     mcptr=a.mcptr;
00106     checkOutCnt=0;
00107     if(a.checkOutCnt>0)
00108       checkout();
00109     return *this;
00110   }
00111   
00112   //! So you can check out if not done by default (or you checked in already)
00113   /*! @param throwOnNULL if true, indicates an exception should be thrown if the checked out motion is NULL (indicates #mc_id does not reference an active MC) */
00114   inline MC_t* checkout(bool throwOnNULL=true) {
00115     mcptr=static_cast<MC_t*>(motman->checkoutMotion(mc_id));
00116     if(throwOnNULL && mcptr==NULL)
00117       throw std::runtime_error("MMAccessor attempted to checkout an invalid MC_ID");
00118     checkOutCnt++;
00119     return mcptr;
00120   }
00121 
00122   //! Returns the motion command's address so you can call functions
00123   inline MC_t* mc() const { return mcptr; }
00124 
00125   //! Checks in the motion
00126   /*! Don't forget, you can also just limit the scope using extra { }'s */
00127   inline void checkin() {
00128     if(checkOutCnt>0) {
00129       if(mc_id!=MotionManager::invalid_MC_ID)
00130         motman->checkinMotion(mc_id);
00131       checkOutCnt--;
00132     }
00133     if(checkOutCnt==0)
00134       mcptr=NULL; // fail fast if we use it after last checkin
00135   }
00136 
00137   //! Checks in the motion, passing through the value it is passed.
00138   /*! @return the same value it's passed
00139    *
00140    *  Useful in situations like this:
00141    *  @code
00142    *  MMAccessor<myMC> mine(myMC_id);
00143    *  if(mine.mc()->foo())
00144    *    //do something with motman here
00145    *  @endcode
00146    *  But we want to check in @a mine ASAP - if we don't reference it
00147    *  anywhere in the if statement, we're leaving the MC locked longer
00148    *  than we need to.  How about instead doing this:
00149    *  @code
00150    *  bool cond;
00151    *  {MMAccessor<myMC> mine(myMC_id); cond=mine.mc()->foo();}
00152    *  if(cond)
00153    *    //do something with motman here
00154    *  @endcode
00155    *  But that uses an extra variable... ewwww... so use this function
00156    *  as a pass through to checkin the MC:
00157    *  @code
00158    *  MMAccessor<myMC> mine(myMC_id);
00159    *  if(mine.checkin(mine.mc()->foo()))
00160    *    //do something with motman here
00161    *  @endcode */
00162   template<class Ret_t> Ret_t checkin(Ret_t ret) {
00163     checkin();
00164     return ret;
00165   }
00166 
00167   MC_t* operator->() { return mc(); } //!< smart pointer to the underlying MotionCommand
00168   const MC_t* operator->() const { return mc(); } //!< smart pointer to the underlying MotionCommand
00169   MC_t& operator*() { return *mc(); } //!< smart pointer to the underlying MotionCommand
00170   const MC_t& operator*() const { return *mc(); } //!< smart pointer to the underlying MotionCommand
00171   MC_t& operator[](int i) { return mc()[i]; } //!< smart pointer to the underlying MotionCommand
00172   const MC_t& operator[](int i) const { return mc()[i]; } //!< smart pointer to the underlying MotionCommand
00173 
00174  protected:
00175   MotionManager::MC_ID mc_id; //!< the MC_ID that this Accessor was constructed with
00176   unsigned int checkOutCnt;   //!< counter so we know how many times checkout was called
00177   MC_t* mcptr;                //!< a pointer to the motion command, should always be valid even when not checked out so you can access member fields (which is reasonably safe)
00178 };
00179 
00180 /*! @file
00181  * @brief Defines MMAccessor, allows convenient ways to check MotionCommands in and out of the MotionManager
00182  * @author ejt (Creator)
00183  */
00184 
00185 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:45 2016 by Doxygen 1.6.3