Handout
This commit is contained in:
293
object/queue.h
Normal file
293
object/queue.h
Normal file
@@ -0,0 +1,293 @@
|
||||
/*! \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 << "}";
|
||||
}
|
||||
Reference in New Issue
Block a user