/*! \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) and next-pointers embedded * in the queued objects. By passing a different get_link function into the * constructor, the member name of the next-pointer can be changed and objects * can be contained in different independent queues. */ template class Queue { /// Default get_link implementation returns a pointer to the next-pointer. static T** default_get_link(T& obj) { return &obj.queue_link; } /// Type definition for the get_link function typedef T** (*NextFunc)(T&); /// 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& queue; T* current; friend class Queue; Iterator(Queue& 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] 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(NextFunc get_link = default_get_link) : get_link(get_link), head(nullptr), tail(nullptr) { assert(get_link != nullptr && "get_link function pointer must not be 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(item); if (*nextptr != nullptr || (head != nullptr && tail == &item)) { return false; } *nextptr = nullptr; if (head == nullptr) { head = tail = &item; } else { assert(tail != nullptr); *get_link(*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(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(item); // if item is already in the list return false if (*nextptr != nullptr || tail == &item) { return false; } T** pnextptr = get_link(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(item); 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(*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(*head); *get_link(*that) = nullptr; return that; } while (cur) { next_link = get_link(*cur); if (*next_link == that) { *next_link = *get_link(**next_link); if (that == tail) { tail = cur; } *get_link(*that) = nullptr; return that; } cur = *next_link; } return nullptr; } /// get an iterator to the first element Queue::Iterator begin() { return Queue::Iterator(*this, head); } /// get an iterator that marks the end of list Queue::Iterator end() { return Queue::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 OutputStream& operator<<(OutputStream& os, Queue& queue) { os << "{"; for (typename Queue::Iterator it = queue.begin(); it != queue.end(); ++it) { os << *it; if (it + 1 != queue.end()) { os << ", "; } } return os << "}"; }