Template class to implement a list with a fixed maximum number of elements (i.e. number of elements in the list is dynamic but cannot exceed the initially defined value)
Dependents: FixedLengthListTest XBeeApi
FixedLengthList.hpp
- Committer:
- johnb
- Date:
- 2014-01-19
- Revision:
- 1:d98987d1d67c
- Parent:
- 0:99d701354221
- Child:
- 2:16c77b601175
File content as of revision 1:d98987d1d67c:
/** @file @brief Template class ( FixedLengthList ) to implement a list with a limited number of elements. @author John Bailey @copyright Copyright 2013 John Bailey @section LICENSE Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #if !defined FIXEDLENGTHLIST_HPP #define FIXEDLENGTHLIST_HPP #include <cstddef> // for size_t, NULL #include <cstring> // For memset() #include <algorithm> // for min() #ifndef STATIC_ASSERT /** Emulation of C++11's static_assert */ #define STATIC_ASSERT( condition, name ) typedef char assert_failed_ ## name [ (condition) ? 1 : -1 ] #endif /* Each item in the FixedLengthList is wrapped in a FixedLengthListItem which provides the actual item and infrastructure support for the list */ template < class L > class FixedLengthListItem { public: /** Pointer to the next item in the list */ FixedLengthListItem<L>* m_forward; /** The content/value of the item itself */ L m_item; }; /** Iterator support class for FixedLengthList Example: \code #define LIST_LEN (20U) FixedLengthList<int, LIST_LEN > list; int main( void ) { // List is empty list.queue( 111 ); list.queue( 222 ); FixedLengthList<int, LIST_LEN>::iterator it = list.begin(); *it == 111; it++; *it == 222; it++; *it == list.end(); } \endcode */ template< class T, size_t queueMax > class FixedLengthListIter { protected: /** The iterator hooks into the used list within the FixedLengthList */ FixedLengthListItem<T>* m_item; public: /** Void constructor - iterator will be equal to T::end() */ FixedLengthListIter( void ); /** Construct an iterator which points to a list item within a FixedLengthList */ FixedLengthListIter( FixedLengthListItem<T>* p_item ); /** De-reference operator, yields the value of the list item */ T& operator*(); /** Inequality operator */ bool operator!=( const FixedLengthListIter& p_comp ) const; /** Equality operator */ bool operator==( const FixedLengthListIter& p_comp ) const; /** Move the iterator forward a specified number of list elements \param p_inc Number of items to traverse */ FixedLengthListIter& operator+=( const unsigned p_inc ); /** Post-increment operator */ FixedLengthListIter operator++( int p_int ); /** Pre-increment operator */ FixedLengthListIter& operator++( void ); }; /** Template class to implement a list with a fixed maximum number of elements (i.e. the number of elements in the list is variable but cannot exceed a defined maximum). While stl::list may be suitable for many occasions, it invariably makes use of dynamic memory allocation which can either be undesirable or overkill in some situations. Of course, the down side of this class is that the memory required to store all items in the list is allocated for the duration of the instantiation. The implementation is based around a singly linked list with a stack of free elements. Adding an item to the list causes one of the free elements to be popped, populated then inserted into the linked list of used elements. For convenience both a head and tail pointer of the used list are maintained. Note that the class currently is not thread safe. Example: \code #define LIST_LEN (20U) FixedLengthList<int, LIST_LEN > list; int main( void ) { int i; // List is empty list.queue( 111 ); // List now contains 111 list.queue( 222 ); // List now contains 111, 222 list.push( 333 ); // List now contains 333, 111, 222 list.pop( &i ); // i == 333 // List now contains 111, 222 return 0; } \endcode */ template < class T, size_t queueMax > class FixedLengthList { /* Pointless to have a queue with no space in it, so the various methods shouldn't have to deal with this situation */ STATIC_ASSERT( queueMax > 0, Queue_must_have_a_non_zero_length ); private: /** Pool of list items */ FixedLengthListItem<T> m_items[ queueMax ]; /** Pointer to the start of the queue of free list slots. Will be NULL in the case that there none are available */ FixedLengthListItem<T>* m_freeHead; /** Pointer to the first item in the list of utilised item slots. Will be NULL in the case that there are none */ FixedLengthListItem<T>* m_usedHead; /** Pointer to the last item in the list of utilised item slots. Will be NULL in the case that there are none */ FixedLengthListItem<T>* m_usedTail; /** Keep count of the number of used items on the list. Ranges between 0 and queueMax */ size_t m_usedCount; public: /** Constructor for FixedLengthList */ FixedLengthList( void ); /** Initialising constructor for FixedLengthList. Parameters will be used to initialise the list \param p_items An array of items used to initialise the list. They will be added in the order in which they appear in p_items \param p_count The number of items in p_items. Only up to queueMax items will be used - any additional items will be ignored */ FixedLengthList( const T* const p_items, size_t p_count ); /** push an item onto the front of the list \param p_item The item to be added to the list \returns true in the case that the item was added false in the case that the item was not added (no space) */ bool push( const T p_item ); /** pop an item from the front of the list (item is removed and returned \param p_item Pointer to be populated with the value of the item \returns true in the case that an item was returned false in the case that an item was not returned (list empty) */ bool pop( T* const p_item ); /** queue an item onto the end of the list \param p_item The item to be added to the list \returns true in the case that the item was added false in the case that the item was not added (no space) */ bool queue( const T p_item ); /** dequeue an item from the end of the list (item is removed and returned \param p_item Pointer to be populated with the value of the item \returns true in the case that an item was returned false in the case that an item was not returned (list empty) */ bool dequeue( T* const p_item ); /** Used to find out how many items are in the list \returns Number of used items, ranging from 0 to queueMax */ size_t used() const; /** Used to find out how many slots are still available in the list \returns Number of available slots, ranging from 0 to queueMax */ size_t available() const; /** Determine whether or not a particular item is in the list \param p_val Item to be matched against \returns true in the case that the item is found in the list false in the case that it is not found in the list */ bool inList( const T p_val ) const; /** Remove the entire contents of the list and return it back to an empty state */ void clear( void ); typedef FixedLengthListIter<T, queueMax> iterator; typedef T value_type; typedef T * pointer; typedef T & reference; iterator begin( void ); iterator end( void ); }; template < class T, size_t queueMax > FixedLengthList< T, queueMax >::FixedLengthList( void ) { clear(); } template < class T, size_t queueMax > FixedLengthList< T, queueMax >::FixedLengthList( const T* const p_items, size_t p_count ) { const T* src = p_items; /* Can only populate up to queueMax items */ size_t init_count = std::min( queueMax, p_count ); FixedLengthListItem<T>* prev = NULL; m_usedHead = NULL; m_usedTail = NULL; /* Initialise the list from p_items, building the forward links */ for( size_t i = 0; i < init_count; i++ ) { FixedLengthListItem<T>* current = &(m_items[i]); current->m_forward = NULL; current->m_item = *(src++); /* If there was a previous item in the list, set up its forward pointer, otherwise set up the list head */ if( prev != NULL ) { prev->m_forward = current; } else { m_usedHead = current; } m_usedTail = current; prev = current; } m_usedCount = init_count; m_freeHead = NULL; /* Any remaining items get moved into the free stack */ prev = NULL; for( size_t i = init_count; i < queueMax; i++ ) { FixedLengthListItem<T>* current = &(m_items[i]); current->m_forward = NULL; if( prev != NULL ) { prev->m_forward = current; } else { m_freeHead = current; } prev = current; } } template < class T, size_t queueMax > void FixedLengthList< T, queueMax >::clear( void ) { FixedLengthListItem<T>* p; size_t i; m_usedHead = NULL; m_usedTail = NULL; m_freeHead = m_items; /* Move all items into the free stack, setting up the forward links */ for( p = m_items, i = (queueMax-1); i > 0 ; i-- ) { FixedLengthListItem<T>* next = p+1; p->m_forward = next; p = next; } p->m_forward = NULL; m_usedCount = 0U; } template < class T, size_t queueMax > bool FixedLengthList< T, queueMax >::push( const T p_item ) { bool ret_val = false; /* Check that there's space in the list */ if( m_freeHead != NULL ) { FixedLengthListItem<T>* new_item = m_freeHead; /* Move the head pointer to the next free item in the list */ m_freeHead = new_item->m_forward; new_item->m_forward = m_usedHead; new_item->m_item = p_item; m_usedHead = new_item; if( m_usedTail == NULL ) { m_usedTail = new_item; } m_usedCount++; /* Indicate success */ ret_val = true; } return ret_val; } template < class T, size_t queueMax > bool FixedLengthList< T, queueMax >::queue( const T p_item ) { bool ret_val = false; /* Check that there's space in the list */ if( m_freeHead != NULL ) { /* Grab a free item */ FixedLengthListItem<T>* new_item = m_freeHead; /* Move the head pointer to the next free item in the list */ m_freeHead = m_freeHead->m_forward; /* Item is going at end of list - no forward link */ new_item->m_forward = NULL; new_item->m_item = p_item; /* Update the current tail item, if exists */ if( m_usedTail != NULL ) { m_usedTail->m_forward = new_item; } m_usedTail = new_item; if( m_usedHead == NULL ) { m_usedHead = new_item; } m_usedCount++; /* Indicate success */ ret_val = true; } return ret_val; } template < class T, size_t queueMax > bool FixedLengthList< T, queueMax >::pop( T* const p_item ) { bool ret_val = false; if( m_usedHead != NULL ) { FixedLengthListItem<T>* old_item = m_usedHead; *p_item = old_item->m_item; m_usedHead = old_item->m_forward; if( m_usedTail == old_item ) { m_usedTail = NULL; } old_item->m_forward = m_freeHead; m_freeHead = old_item; m_usedCount--; /* Indicate success */ ret_val = true; } return ret_val; } template < class T, size_t queueMax > bool FixedLengthList< T, queueMax >::dequeue( T* const p_item ) { bool ret_val = false; if( m_usedTail != NULL ) { FixedLengthListItem<T>* old_item = m_usedTail; *p_item = old_item->m_item; /* Item removed was only item in the list? */ if( m_usedHead == old_item ) { m_usedHead = NULL; m_usedTail = NULL; } else { FixedLengthListItem<T>* p = m_usedHead; /* Need to update the forward pointer of the item preceding the one which we've just removed. That item also becomes the new tail Iterate the list and find it */ while( p != NULL ) { if( p->m_forward == old_item ) { p->m_forward = NULL; m_usedTail = p; break; } else { p = p->m_forward; } } } /* Move item to free list */ old_item->m_forward = m_freeHead; m_freeHead = old_item; m_usedCount--; /* Indicate success */ ret_val = true; } return ret_val; } template < class T, size_t queueMax > size_t FixedLengthList< T, queueMax >::used() const { return m_usedCount; } template < class T, size_t queueMax > size_t FixedLengthList< T, queueMax >::available() const { return queueMax - m_usedCount; } template < class T, size_t queueMax > bool FixedLengthList< T, queueMax >::inList( const T p_val ) const { bool ret_val = false; FixedLengthListItem<T>* p = m_usedHead; /* Ordered iteration of the list checking for specified item */ while( p != NULL ) { if( p->m_item == p_val ) { /* Item found - flag and break out */ ret_val = true; break; } else { p = p->m_forward; } } return ret_val; } template < class T, size_t queueMax > FixedLengthListIter<T, queueMax> FixedLengthList< T,queueMax >::begin( void ) { return iterator( m_usedHead ); } template < class T, size_t queueMax > FixedLengthListIter<T, queueMax> FixedLengthList< T,queueMax >::end( void ) { return iterator( NULL ); } template < class T, size_t queueMax > FixedLengthListIter< T, queueMax >::FixedLengthListIter( void ) : m_item( NULL) { } template < class T, size_t queueMax > FixedLengthListIter< T, queueMax >::FixedLengthListIter( FixedLengthListItem<T>* p_item ) : m_item( p_item ) { } template < class T, size_t queueMax > T& FixedLengthListIter< T, queueMax >::operator*() { return m_item->m_item; } template < class T, size_t queueMax > FixedLengthListIter< T,queueMax > FixedLengthListIter< T,queueMax >::operator++( int p_int ) { FixedLengthListIter< T,queueMax > clone( *this ); m_item = m_item->m_forward; return clone; } template < class T, size_t queueMax > FixedLengthListIter< T,queueMax >& FixedLengthListIter< T,queueMax >::operator++( void ) { m_item = m_item->m_forward; return *this; } template < class T, size_t queueMax > FixedLengthListIter< T,queueMax >& FixedLengthListIter< T,queueMax >::operator+=( const unsigned p_inc ) { for(unsigned i = 0; i < p_inc; i++ ) { if( m_item == NULL ) { break; } else { m_item = m_item->m_forward; } } return *this; } template < class T, size_t queueMax > bool FixedLengthListIter< T,queueMax >::operator==( const FixedLengthListIter& p_comp ) const { return m_item == p_comp.m_item; } template < class T, size_t queueMax > bool FixedLengthListIter< T,queueMax >::operator!=( const FixedLengthListIter& p_comp ) const { return m_item != p_comp.m_item; } #endif