BasicQueue_T.h

Go to the documentation of this file.
00001 /*
00002  *
00003  *
00004  * Distributed under the OpenDDS License.
00005  * See: http://www.opendds.org/license.html
00006  */
00007 
00008 #ifndef OPENDDS_DCPS_BASICQUEUE_T_H
00009 #define OPENDDS_DCPS_BASICQUEUE_T_H
00010 
00011 #include "BasicQueueLinkPool_T.h"
00012 #include "BasicQueueVisitor_T.h"
00013 #include "dds/DCPS/PoolAllocationBase.h"
00014 
00015 namespace OpenDDS {
00016 namespace DCPS {
00017 
00018 template <typename T>
00019 class BasicQueue : public PoolAllocationBase {
00020 private:
00021 
00022   typedef BasicQueueLinkPool<T> PoolType;
00023   typedef BasicQueueLink<T>     LinkType;
00024   typedef BasicQueueVisitor<T>  VisitorType;
00025 
00026 public:
00027 
00028   BasicQueue(size_t links_per_pool, size_t initial_pools)
00029   : head_(0),
00030     tail_(0),
00031     size_(0),
00032     pool_(links_per_pool, initial_pools)
00033   {}
00034 
00035   ~BasicQueue() {
00036   }
00037 
00038   /// Put a pointer to an element (T*) on to the queue.
00039   int put(T* elem) {
00040     // Get a "new" link from the pool.
00041     LinkType* link = pool_.obtain(elem);
00042 
00043     if (link == 0) {
00044       // Shouldn't happen unless we run out of memory (or encounter
00045       // an internal error condition of some sort, making the
00046       // pool_ unable to provide us with a valid link).
00047       return -1;
00048     }
00049 
00050     // Insert the link on the the end of the queue.
00051     if (this->tail_ == 0) {
00052       // The tail_ is NULL only when the queue is empty.
00053       // Set both the head_ and tail_ to point to the "new" link.
00054       this->head_ = this->tail_ = link;
00055 
00056     } else {
00057       // There is at least one element in the queue.
00058       // Set the next() of the current tail_ link to point to
00059       // the "new" link.  Then set the tail_ to point to the
00060       // "new" link.
00061       this->tail_->next(link);
00062       this->tail_ = link;
00063     }
00064 
00065     // Increment our size_ by one.
00066     ++this->size_;
00067 
00068     return 0;
00069   }
00070 
00071   /// Peek at the element at the top of the queue.  This is just
00072   /// like the get() operation except that the queue remains intact.
00073   T* peek() const {
00074     if (this->head_ == 0) {
00075       // The queue is empty.  Return a NULL pointer (0).
00076       return 0;
00077     }
00078 
00079     return this->head_->elem();
00080   }
00081 
00082   void replace_head(T* value) {
00083     if (this->head_ != 0) {
00084       this->head_->elem(value);
00085     }
00086   }
00087 
00088   /// Extract the top element from the queue.  Returns 0 if there
00089   /// are no elements in the queue.
00090   T* get() {
00091     LinkType* link = this->head_;
00092 
00093     if (link == 0) {
00094       // The queue is empty.  Return a NULL pointer (0).
00095       return 0;
00096     }
00097 
00098     // Adjust the head_ (and possibly the tail_ if there is only
00099     // one link currently in the queue).
00100     this->head_ = link->next();
00101 
00102     if (this->head_ == 0) {
00103       // That was the only link in the queue.  Now that the head_
00104       // indicates that the queue is empty, we need to update the
00105       // tail_ accordingly because we know it was pointing to the
00106       // same link as the head_.
00107       this->tail_ = 0;
00108     }
00109 
00110     // Save off the pointer to the link's elem().
00111     T* elem = link->elem();
00112 
00113     // Release the link object back to the pool.
00114     this->pool_.release(link);
00115 
00116     // Decrement our size_ by one.
00117     --this->size_;
00118 
00119     // And finally return the elem.
00120     return elem;
00121   }
00122 
00123   /// Accessor for the current number of elements in the queue.
00124   size_t size() const {
00125     return this->size_;
00126   }
00127 
00128   /// Standard way to supply a visitor to the queue - this will
00129   /// invoke visit_element(T* element) on the supplied visitor object
00130   /// once for each element in this BasicQueue<T> object, in order.
00131   /// The visitor can stop visitation early by returning 0 from
00132   /// its visit_element(T* element) method.
00133   void accept_visitor(VisitorType& visitor) const {
00134     LinkType* link = this->head_;
00135 
00136     while (link != 0) {
00137       if (visitor.visit_element(link->elem()) == 0) {
00138         return;
00139       }
00140 
00141       link = link->next();
00142     }
00143   }
00144 
00145   /// Alternate way to supply a visitor to the queue - this will
00146   /// invoke visit_element(T* element, int& remove) on the supplied
00147   /// visitor object once for each element in this BasicQueue<T>
00148   /// object, in order.
00149   ///
00150   /// The remove argument is a flag that should be set to true (1)
00151   /// in the visitor's visit_element_remove(T* element, int& remove)
00152   /// method if the visitor decides that the element should be removed
00153   /// from the queue.  The remove flag is always set to false (0)
00154   /// prior to calling the visitor's visit_element_remove(T* element,
00155   /// int& remove) method.
00156   ///
00157   /// The visitor can stop visitation early by returning 0 from
00158   /// its visit_element_remove(T* element, int& remove) method.
00159   void accept_remove_visitor(VisitorType& visitor) {
00160     LinkType* prev_link = 0;
00161     LinkType* link = this->head_;
00162 
00163     while (link != 0) {
00164       // Save off the next_link now, in case we end up removing
00165       // (and releasing) the current link via a call to our
00166       // remove_next() call further on.
00167       LinkType* next_link = link->next();
00168 
00169       T* element = link->elem();
00170 
00171       int remove = 0;
00172 
00173       int keep_going = visitor.visit_element_remove(element, remove);
00174 
00175       if (remove == 1) {
00176         this->remove_next(prev_link);
00177 
00178         // We shouldn't change the prev_link here, because it
00179         // has become the prev_link of the next_link since the
00180         // (middle-man) link was just removed.
00181 
00182       } else {
00183         // Set it up for visitation of the next link.
00184         // Adjust the prev_link here because we didn't remove
00185         // the link.
00186         prev_link = link;
00187       }
00188 
00189       link = next_link;
00190 
00191       if (keep_going == 0) {
00192         // We are done.
00193         return;
00194       }
00195     }
00196   }
00197 
00198   /// This kind of visitation may cause the visitor to replace
00199   /// the currently visited element with a new element.
00200   void accept_replace_visitor(VisitorType& visitor) {
00201     LinkType* link = this->head_;
00202 
00203     while (link != 0) {
00204       // The subtle difference between this visitation method
00205       // and the normal "accept_visitor" visitation method is
00206       // that we supply a reference to a pointer to an element
00207       // here rather than just a pointer to an element.  This
00208       // allows the visitor to "replace" the current element
00209       // with a new element.
00210       if (visitor.visit_element_ref(link->elem_ref()) == 0) {
00211         return;
00212       }
00213 
00214       link = link->next();
00215     }
00216   }
00217 
00218 private:
00219 
00220   /// Used by our accept_remove_visitor() method when an element needs
00221   /// to be removed from this BasicQueue<T> object.  The element that
00222   /// is supposed to be removed is the next() link object of the
00223   /// supplied "prev" link object.  It's done this way so that the
00224   /// (singlely) linked-list can be stitched back together after the
00225   /// appropriate link has been removed.
00226   void remove_next(LinkType* prev) {
00227     // We are either removing the head_ or the prev->next().
00228     // Note that we assume that in this contect, head_ will not
00229     // be NULL (0).  And, if prev is not NULL (0), then we assume
00230     // that prev->next() is not NULL (0).
00231     //
00232     // Note that we also assume that the prev link (if not NULL),
00233     // is indeed a member of the queue!
00234     LinkType* link = (prev == 0) ? this->head_ : prev->next();
00235 
00236     // Handle the case where the queue only contains one link
00237     // (we can then assume that this is the link we are removing).
00238     if (this->head_ == this->tail_) {
00239       // We are removing the only link in the queue.
00240       this->head_ = this->tail_ = 0;
00241 
00242     } else if (link == this->head_) {
00243       // We are removing the head link, and we know that there
00244       // are other links still in the queue.
00245       this->head_ = this->head_->next();
00246 
00247     } else if (link == this->tail_) {
00248       // We are removing the tail link, and we know that there
00249       // are other links still in the queue.
00250       prev->next(0);
00251       this->tail_ = prev;
00252 
00253     } else {
00254       // We are removing a link that is neither the head_ link
00255       // nor the tail_ link.
00256       prev->next(link->next());
00257     }
00258 
00259     // Decrement our size by one
00260     --this->size_;
00261 
00262     // And finally, we can release the link back to the pool.
00263     this->pool_.release(link);
00264   }
00265 
00266   /// The first (oldest) link in the queue.
00267   LinkType* head_;
00268 
00269   /// The last (newest) link in the queue.
00270   LinkType* tail_;
00271 
00272   /// The number of links currently in the queue.
00273   size_t size_;
00274 
00275   /// The pool of precreated LinkType objects (a "self-growing"
00276   /// allocator is used by the pool).
00277   PoolType pool_;
00278 };
00279 
00280 } // namespace DCPS
00281 } // namespace OpenDDS
00282 
00283 #endif  /* OPENDDS_DCPS_BASICQUEUE_T_H */

Generated on Fri Feb 12 20:05:18 2016 for OpenDDS by  doxygen 1.4.7