You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			294 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			294 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
| /*! \file
 | |
|  *	\brief Templated \ref Queue for arbitrary objects.
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| #include "../arch/core.h"
 | |
| #include "../debug/assert.h"
 | |
| #include "../object/outputstream.h"
 | |
| #include "../types.h"
 | |
| 
 | |
| /*! \brief Templated Queue for arbitrary objects.
 | |
|  *
 | |
|  * Queue is implemented by a head-object (Queue<T>) and next-pointers embedded
 | |
|  * in the queued objects. This Queue supports arrays of next-pointers by passing
 | |
|  * an index into the constructor identifying the index into the next-pointer
 | |
|  * array. By passing a different get_link function into the constructor, the
 | |
|  * member name of the next-pointer array can be changed and objects can be
 | |
|  * contained in different independent queues.
 | |
|  */
 | |
| template <class T>
 | |
| class Queue {
 | |
|   /*! \brief Default get_link implementation returns a pointer to the
 | |
|    * link_index'th element of the next-pointer array.
 | |
|    * The function assumes a member named "queue_link" that stores the
 | |
|    * next-pointer.
 | |
|    *
 | |
|    * If your object contains a queue_link member you can just ignore this
 | |
|    * function and the get_link keyword argument of the constructor.
 | |
|    *
 | |
|    * \param[in] obj the object whose link should be accessed.
 | |
|    * \param[in] link_index the index within the array.
 | |
|    *
 | |
|    * \return A pointer to the next-object pointer.
 | |
|    */
 | |
|   static T** default_get_link(T& obj, unsigned link_index) {
 | |
|     assert(link_index < sizeof(T::queue_link) / sizeof(void*));
 | |
|     return &obj.queue_link[link_index];
 | |
|   }
 | |
|   /// Type definition for the get_link function
 | |
|   typedef T** (*NextFunc)(T&, unsigned);
 | |
| 
 | |
|   /// Queue-local index into the next-pointer array
 | |
|   unsigned link_index;
 | |
| 
 | |
|   /// Provides the same signature for single- and multi-core Queue
 | |
|   T** get_link_wrapped(T& obj) { return get_link(obj, link_index); }
 | |
| 
 | |
|   /// Function pointer to the get_link function, called whenever the
 | |
|   /// next pointer array is referenced
 | |
|   const NextFunc get_link;
 | |
|   /// head points to the first element (the one returned on first dequeue).
 | |
|   /// Can be nullptr if the queue is empty.
 | |
|   T* head;
 | |
|   /// tail points to the last element (the one last added).
 | |
|   /// Is only valid if head != nullptr
 | |
|   T* tail;
 | |
| 
 | |
|   // Prevent copies and assignments
 | |
|   Queue(const Queue&) = delete;
 | |
|   Queue& operator=(const Queue&) = delete;
 | |
| 
 | |
|  public:
 | |
|   /*! \brief Minimal forward iterator
 | |
|    * You can use this iterator to iterate the queue like a normal STL container.
 | |
|    * It only supports forward iteration, since the queue is single linked.
 | |
|    */
 | |
|   class Iterator {
 | |
|    private:
 | |
|     Queue<T>& queue;
 | |
|     T* current;
 | |
|     friend class Queue<T>;
 | |
|     Iterator(Queue<T>& queue, T* current) : queue(queue), current(current) {}
 | |
| 
 | |
|    public:
 | |
|     Iterator operator+(unsigned num) {
 | |
|       if (current == nullptr) {
 | |
|         return *this;
 | |
|       }
 | |
|       T* temp = current;
 | |
|       while (num--) {
 | |
|         temp = queue.next(*temp);
 | |
|       }
 | |
|       return Iterator(queue, temp);
 | |
|     }
 | |
| 
 | |
|     // pre increment
 | |
|     Iterator& operator++() {
 | |
|       current = queue.next(*current);
 | |
|       return *this;
 | |
|     }
 | |
| 
 | |
|     // post increment
 | |
|     Iterator operator++(int) {
 | |
|       auto temp = Iterator(queue, current);
 | |
|       current = queue.next(*current);
 | |
|       return temp;
 | |
|     }
 | |
| 
 | |
|     T* operator*() { return current; }
 | |
| 
 | |
|     bool operator==(const Iterator& other) { return current == other.current; }
 | |
| 
 | |
|     bool operator!=(const Iterator& other) { return !(*this == other); }
 | |
|   };
 | |
| 
 | |
|   constexpr Queue(Queue&&) = default;
 | |
| 
 | |
|   /*! \brief Constructor
 | |
|    *	\param[in] link_index denotes the index into the next-pointer array
 | |
|    *						  to be used by this
 | |
|    *queue-object
 | |
|    *	\param[in] get_link A function pointer to the get_link, i.e. a function
 | |
|    *						which returns a pointer to the
 | |
|    *next-pointer of an element in the Queue.
 | |
|    */
 | |
|   explicit Queue(unsigned link_index, NextFunc get_link = default_get_link)
 | |
|       : link_index(link_index),
 | |
|         get_link(get_link),
 | |
|         head(nullptr),
 | |
|         tail(nullptr) {}
 | |
| 
 | |
|   /*! \brief Enqueues the provided item at the end of the queue. If the element
 | |
|    *	is already contained in the queue, false will be returned
 | |
|    * \param[in] item	element to be appended (enqueued).
 | |
|    * \return false if the element already was enqueued (and nothing was done)
 | |
|    *		   or not (and it is now enqueued, then true)
 | |
|    */
 | |
|   bool enqueue(T& item) {
 | |
|     T** nextptr = get_link_wrapped(item);
 | |
|     if (*nextptr != nullptr || (head != nullptr && tail == &item)) {
 | |
|       return false;
 | |
|     }
 | |
|     *nextptr = nullptr;
 | |
| 
 | |
|     if (head == nullptr) {
 | |
|       head = tail = &item;
 | |
|     } else {
 | |
|       assert(tail != nullptr);
 | |
|       *get_link_wrapped(*tail) = &item;
 | |
|       tail = &item;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /*! \brief insert a new element at the start of the queue
 | |
|    *  \param[in] item the new item to add
 | |
|    *  \return true if successful, false if item was already in the queue
 | |
|    **/
 | |
|   bool insertFirst(T& item) {
 | |
|     T** nextptr = get_link_wrapped(item);
 | |
|     if (*nextptr != nullptr || (head != nullptr && tail == &item)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (head == nullptr) {
 | |
|       tail = &item;
 | |
|     }
 | |
|     *nextptr = head;
 | |
|     head = &item;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /*! \brief Insert a new element item into the list after an element after.
 | |
|    *  Returns false if item is already in the/a list or after is not in this
 | |
|    *list
 | |
|    *  \param[in] after the element after which the new one should be inserted
 | |
|    *  \param[in] item the new element to add
 | |
|    *  \return true if successful, false if item was in the list or after was not
 | |
|    **/
 | |
|   bool insertAfter(T& after, T& item) {
 | |
|     // if queue is empty there is no after
 | |
|     // and tail is not valid so we need to check head here
 | |
|     if (head == nullptr) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (&after == tail) {
 | |
|       return enqueue(item);
 | |
|     }
 | |
|     T** nextptr = get_link_wrapped(item);
 | |
|     // if item is already in the list return false
 | |
|     if (*nextptr != nullptr || tail == &item) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     T** pnextptr = get_link_wrapped(after);
 | |
|     // if after is NOT in the list, return false
 | |
|     if (!(pnextptr != nullptr || tail == &after)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     *nextptr = *pnextptr;
 | |
|     *pnextptr = &item;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /*! \brief return the next element of a given one or nullptr if the end is
 | |
|    *reached
 | |
|    *  \param[in] item the current item
 | |
|    *  \return the next element or nullptr if the end is reached or the item is
 | |
|    *not in this list
 | |
|    **/
 | |
|   T* next(T& item) {
 | |
|     T** nextptr = get_link_wrapped(item);
 | |
|     // if item is already in the list return nullptr
 | |
|     if (head == nullptr || (*nextptr == nullptr && tail != &item)) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     return *nextptr;
 | |
|   }
 | |
| 
 | |
|   /*! \brief Return whether or not the queue is empty
 | |
|    * \return True if the queue is empty or false otherwise.
 | |
|    */
 | |
|   bool is_empty() const { return (head == nullptr); }
 | |
| 
 | |
|   /*! \brief Removes the first element in the queue and returns it.
 | |
|    *  \note Does not update the tail-pointer
 | |
|    *	\return Pointer to the removed item or `nullptr` if the queue was empty.
 | |
|    */
 | |
|   T* dequeue() {
 | |
|     T* out = head;
 | |
|     if (head != nullptr) {
 | |
|       T** nextptr = get_link_wrapped(*head);
 | |
|       head = *nextptr;
 | |
|       *nextptr = nullptr;
 | |
|     }
 | |
|     return out;
 | |
|   }
 | |
| 
 | |
|   /*! \brief Removes a given element from the queue and returns that element,
 | |
|    *  or nullptr if it was not present
 | |
|    *  \return pointer to the removed element, or nullptr if not present
 | |
|    */
 | |
|   T* remove(T* that) {
 | |
|     if (!that) return nullptr;
 | |
|     T* cur = head;
 | |
|     T** next_link;
 | |
| 
 | |
|     if (head == that) {
 | |
|       head = *get_link_wrapped(*head);
 | |
| 
 | |
|       *get_link_wrapped(*that) = nullptr;
 | |
|       return that;
 | |
|     }
 | |
|     while (cur) {
 | |
|       next_link = get_link_wrapped(*cur);
 | |
|       if (*next_link == that) {
 | |
|         *next_link = *get_link_wrapped(**next_link);
 | |
| 
 | |
|         if (that == tail) {
 | |
|           tail = cur;
 | |
|         }
 | |
| 
 | |
|         *get_link_wrapped(*that) = nullptr;
 | |
|         return that;
 | |
|       }
 | |
|       cur = *next_link;
 | |
|     }
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   /// get an iterator to the first element
 | |
|   Queue<T>::Iterator begin() { return Queue<T>::Iterator(*this, head); }
 | |
| 
 | |
|   /// get an iterator that marks the end of list
 | |
|   Queue<T>::Iterator end() { return Queue<T>::Iterator(*this, nullptr); }
 | |
| 
 | |
|   /// get the first element of the queue
 | |
|   T* first() { return head; }
 | |
| 
 | |
|   /// get the last element of the queue
 | |
|   T* last() { return (head == nullptr ? nullptr : tail); }
 | |
| };
 | |
| 
 | |
| /*! \brief Overload stream operator for list printing.
 | |
|  *
 | |
|  * With this a list can be printed. The elements itself are not printed, just
 | |
|  * the pointer.
 | |
|  */
 | |
| template <class T>
 | |
| OutputStream& operator<<(OutputStream& os, Queue<T>& queue) {
 | |
|   os << "{";
 | |
|   for (typename Queue<T>::Iterator it = queue.begin(); it != queue.end();
 | |
|        ++it) {
 | |
|     os << *it;
 | |
|     if (it + 1 != queue.end()) {
 | |
|       os << ", ";
 | |
|     }
 | |
|   }
 | |
|   return os << "}";
 | |
| }
 |