Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
MMAccessor.hGo 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 |