/* support_light_list.ino - Lightweight Linked List for simple objects - optimized for low code size and low memory Copyright (C) 2020 Theo Arends and Stephan Hadinger This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /*********************************************************************************************\ * * private class for Linked List element * \*********************************************************************************************/ template class LList; template class LList_elt { public: LList_elt() : _next(nullptr), _val() {} inline T & val(void) { return _val; } inline LList_elt * next(void) { return _next; } inline void next(LList_elt * next) { _next = next; } friend class LList; protected: LList_elt * _next; T _val; }; /*********************************************************************************************\ * * Lightweight Linked List - optimized for low code size * \*********************************************************************************************/ template class LList { public: LList() : _head(nullptr) {} ~LList() { reset(); } // remove elements void removeHead(void); // remove first element void reset(void); // remove all elements void remove(const T * val); // read the list inline bool isEmpty(void) const { return (_head == nullptr) ? true : false; } size_t length(void) const; inline T * head(void) { return _head ? &_head->_val : nullptr; } inline const T * head(void) const { return _head ? &_head->_val : nullptr; } const T * at(size_t index) const ; // non-const variants // not very academic cast but reduces code size inline T * at(size_t index) { return (T*) ((const LList*)this)->at(index); } // adding elements T & addHead(void); T & addHead(const T &val); T & addToLast(void); // add an element allocated externally // memory is free by us now -- don't free it! T & addHead(LList_elt * elt); T & addToLast(LList_elt * elt); // iterator // see https://stackoverflow.com/questions/8164567/how-to-make-my-custom-type-to-work-with-range-based-for-loops class iterator { public: iterator(LList_elt *_cur): cur(_cur), next(nullptr) { if (cur) { next = cur->_next; } } iterator operator++() { cur = next; if (cur) { next = cur->_next;} return *this; } bool operator!=(const iterator & other) const { return cur != other.cur; } T & operator*() const { return cur->_val; } private: LList_elt *cur; LList_elt *next; // we need to keep next pointer in case the current attribute gets deleted }; iterator begin() { return iterator(this->_head); } // start with 'head' iterator end() { return iterator(nullptr); } // end with null pointer // const iterator class const_iterator { public: const_iterator(const LList_elt *_cur): cur(_cur), next(nullptr) { if (cur) { next = cur->_next; } } const_iterator operator++() { cur = next; if (cur) { next = cur->_next;} return *this; } bool operator!=(const_iterator & other) const { return cur != other.cur; } const T & operator*() const { return cur->_val; } private: const LList_elt *cur; const LList_elt *next; // we need to keep next pointer in case the current attribute gets deleted }; const_iterator begin() const { return const_iterator(this->_head); } // start with 'head' const_iterator end() const { return const_iterator(nullptr); } // end with null pointer protected: LList_elt * _head; }; template size_t LList::length(void) const { size_t count = 0; for (auto & elt : *this) { (void)elt; count++; } return count; } template const T * LList::at(size_t index) const { size_t count = 0; for (const auto & elt : *this) { if (index == count++) { return &elt; } } return nullptr; } template void LList::reset(void) { while (_head) { LList_elt * next = _head->next(); delete _head; _head = next; } } template void LList::removeHead(void) { if (_head) { LList_elt * next = _head->next(); delete _head; _head = next; } } template void LList::remove(const T * val) { if (nullptr == val) { return; } // find element in chain and find pointer before LList_elt **curr_ptr = &_head; while (*curr_ptr) { LList_elt * curr_elt = *curr_ptr; if ( &(curr_elt->_val) == val) { *curr_ptr = curr_elt->_next; // update previous pointer to next element delete curr_elt; break; // stop iteration now } curr_ptr = &((*curr_ptr)->_next); // move to next element } } template T & LList::addHead(void) { LList_elt * elt = new LList_elt(); // create element elt->next(_head); // insert at the head _head = elt; return elt->_val; } template T & LList::addHead(const T &val) { LList_elt * elt = new LList_elt(); // create element elt->next(_head); // insert at the head elt->_val = val; _head = elt; return elt->_val; } template T & LList::addHead(LList_elt * elt) { elt->next(_head); // insert at the head _head = elt; return elt->_val; } template T & LList::addToLast(void) { LList_elt **curr_ptr = &_head; while (*curr_ptr) { curr_ptr = &((*curr_ptr)->_next); } LList_elt * elt = new LList_elt(); // create element *curr_ptr = elt; return elt->_val; } template T & LList::addToLast(LList_elt * elt) { LList_elt **curr_ptr = &_head; while (*curr_ptr) { curr_ptr = &((*curr_ptr)->_next); } *curr_ptr = elt; elt->_next = nullptr; return elt->_val; }