#ifdef USE_API_MAXON #error "Please check your project include paths or your relative include path." #elif !defined(C4DMISC_BASELIST_H__) #define C4DMISC_BASELIST_H__ #include "../memory/defaultallocator.h" namespace maxon { /// @addtogroup STRUCTURES /// @{ struct BaseListLinkPOD { void* _next; UInt _prevFlag; // bit0: listheadflag bit>0 ptr }; //---------------------------------------------------------------------------------------- /// Link field for the pointers of a list node //---------------------------------------------------------------------------------------- template class BaseListLink : public BaseListLinkPOD { public: BaseListLink(NODE* prev = nullptr, NODE* next = nullptr, Bool is_head = false) { _next = next; _prevFlag = ComputePrevFlag(prev, is_head); } #ifdef _DEBUG /// NOTE: the destructor is not virtual ~BaseListLink() { if (_GetNextChecked() || _GetPrevChecked()) DebugStop(); } #endif /// move constructor BaseListLink(BaseListLink&& src) { _next = src._next; _prevFlag = src._prevFlag; src._next = nullptr; src._prevFlag = 0; } /// move assignment operator MOVE_ASSIGNMENT_OPERATOR(BaseListLink) //---------------------------------------------------------------------------------------- /// Gets the next element. /// @return Next element (can never be null, but points to list head if this is the last element) //---------------------------------------------------------------------------------------- NODE* _GetNext() const { return (NODE*) _next; } //---------------------------------------------------------------------------------------- /// Gets the previous element. /// @return Previous element (can never be null, but points to list head if this is the first element) //---------------------------------------------------------------------------------------- NODE* _GetPrev() const { return (NODE*) (_prevFlag & ADDRESS_MASK); } //---------------------------------------------------------------------------------------- /// Removes the element (just removes it from the list, does not delete it). //---------------------------------------------------------------------------------------- static void Remove(NODE* remove) { NODE* p = remove->_GetPrev(); NODE* n = remove->_GetNext(); if (p) p->SetNext(n); if (n) n->SetPrev(p); remove->SetNext(nullptr); remove->SetPrev(nullptr); } //---------------------------------------------------------------------------------------- /// Inserts this before next. /// @param[in] insert Node to insert. /// @param[in] next The next object's link. //---------------------------------------------------------------------------------------- static void InsertBefore(NODE* insert, NODE* next) { NODE* prev = next->_GetPrev(); insert->SetNext(next); insert->SetPrev(prev); prev->SetNext(insert); next->SetPrev(insert); } //---------------------------------------------------------------------------------------- /// Returns true if this is a list head (_GetNext() would return pointer to the first element of the list, _GetPrev() to the last) /// @return True if this is a list head. //---------------------------------------------------------------------------------------- Bool IsListHead() const { return (_prevFlag & HEAD_MASK) != 0; } NODE* _GetNextChecked() const { NODE* n = _GetNext(); return n && !n->IsListHead() ? n : nullptr; } NODE* _GetPrevChecked() const { NODE* p = _GetPrev(); return p && !p->IsListHead() ? p : nullptr; } void SetNext(NODE* val) { _next = (void*) val; } void SetPrev(NODE* val) { _prevFlag &= HEAD_MASK; _prevFlag |= UInt(val) & ADDRESS_MASK; } private: void SetListHead(Bool val) { _prevFlag &= ADDRESS_MASK; _prevFlag |= (UInt)val; } UInt ComputePrevFlag(NODE* prev, Bool head) const { return (UInt(prev) & ADDRESS_MASK) | (UInt)head; } private: static const UInt HEAD_MASK = 1; static const UInt ADDRESS_MASK = ~HEAD_MASK; }; template ::isSupported> class BaseListNode {}; //---------------------------------------------------------------------------------------- /// Template for list node containing element data of type T (T has a copy constructor that cannot fail). /// /// If you have to define a custom list node (which should rarely be the case) you can /// simply use the IMPLEMENT_CUSTOM_BASELISTNODE(YourClass, PointerToBaseListPOD) macro that /// will implement the required methods. /// /// @tparam T Content of the list node. //---------------------------------------------------------------------------------------- template class BaseListNode : public BaseListLink > { public: BaseListNode() : _data() { } BaseListNode(const T& src) : _data(src) { } BaseListNode(T&& src) : _data(std::move(src)) { } ~BaseListNode() { Remove(); } //---------------------------------------------------------------------------------------- /// Removes element (just removes it from the list, does not delete it). //---------------------------------------------------------------------------------------- void Remove() { BaseListLink >::Remove(this); } //---------------------------------------------------------------------------------------- /// Inserts this before next. /// @param[in] next The next object's link. //---------------------------------------------------------------------------------------- void InsertBefore(BaseListNode* next) { BaseListLink >::InsertBefore(this, next); } /// returns element data of the list node T* _GetData() const { return &_data; } BaseListLinkPOD* _GetLink() const { return (BaseListLink >*) this; } protected: mutable T _data; }; //---------------------------------------------------------------------------------------- /// Template for list node containing element data of type T (to copy T you must use CopyFrom) /// /// If you have to define a custom list node (which should rarely be the case) you can /// simply use the IMPLEMENT_CUSTOM_BASELISTNODE(YourClass, PointerToBaseListPOD) macro that /// will implement the required methods. /// /// @tparam T Content of the list node. //---------------------------------------------------------------------------------------- template class BaseListNode : public BaseListLink > { public: BaseListNode() : _data() { } BaseListNode(const T& src) { DebugStop(); // this is a dummy, instead _data.CopyFrom() has to be used } BaseListNode(T&& src) : _data(std::move(src)) { } ~BaseListNode() { Remove(); } //---------------------------------------------------------------------------------------- /// Removes element (just removes it from the list, does not delete it). //---------------------------------------------------------------------------------------- void Remove() { BaseListLink >::Remove(this); } //---------------------------------------------------------------------------------------- /// Inserts this before next. /// @param[in] next The next object's link. //---------------------------------------------------------------------------------------- void InsertBefore(BaseListNode* next) { BaseListLink >::InsertBefore(this, next); } /// returns element data of the list node T* _GetData() const { return &_data; } BaseListLinkPOD* _GetLink() const { return (BaseListLink >*) this; } protected: mutable T _data; }; //---------------------------------------------------------------------------------------- /// Template for list head pointing to elements of T. /// @tparam T Content of the list node. /// @tparam NODE List node containing T. //---------------------------------------------------------------------------------------- template class BaseListHead : public BaseListLink { public: BaseListHead() : BaseListLink(End(), End(), true) { } ~BaseListHead() { CriticalAssert(GetFirst() == nullptr); // all elements must be flushed by the derived template (they know the allocator and can implement a virtual destructor if necessary) } /// move constructor BaseListHead(BaseListHead&& src) : BaseListLink(std::move(src)) { } /// move assignment operator MOVE_ASSIGNMENT_OPERATOR(BaseListHead) //---------------------------------------------------------------------------------------- /// Gets the first element. /// @return First element or nullptr. //---------------------------------------------------------------------------------------- T* GetFirst() const { NODE* n = (NODE*) this->_GetNext(); return n == End() ? nullptr : n->_GetData(); } //---------------------------------------------------------------------------------------- /// Gets the last element. /// @return Last element or nullptr. //---------------------------------------------------------------------------------------- T* GetLast() const { NODE* p = (NODE*) this->_GetPrev(); return p == End() ? nullptr : p->_GetData(); } //---------------------------------------------------------------------------------------- /// Gets the number of elements. /// @return Number of elements. //---------------------------------------------------------------------------------------- Int GetCount() const { Int cnt = 0; for (NODE* e = Begin(); e != End(); e = e->_GetNext()) cnt++; return cnt; } //---------------------------------------------------------------------------------------- /// Gets the element by index. /// @return Element or nullptr (not element of this) //---------------------------------------------------------------------------------------- T* GetElement(Int idx) const { NODE* node = GetNode(idx); return node ? node->_GetData() : nullptr; } NODE* GetNode(Int idx) const { for (NODE* e = Begin(); e != End(); e = e->_GetNext(), idx--) { if (idx == 0) return e; } return nullptr; } //---------------------------------------------------------------------------------------- /// Gets the index of the element. The element must be part of the array, otherwise (e.g. /// if x is a copy of an array element) InvalidArrayIndex will be returned. /// @return Index of element or InvalidArrayIndex (not element of this) //---------------------------------------------------------------------------------------- Int GetIndex(const T* x) const { Int idx = 0; for (NODE* e = Begin(); e != End(); e = e->_GetNext(), idx++) { if (x == e->_GetData()) return idx; } return InvalidArrayIndex; } //---------------------------------------------------------------------------------------- /// Gets the pointer to the first node. /// @return Pointer to the first element (equal to End() if the list is empty) //---------------------------------------------------------------------------------------- NODE* Begin() const { return this->_GetNext(); } //---------------------------------------------------------------------------------------- /// Gets the pointer to the virtual end node (the node after the last node). This is the address /// of a virtual (non-existing) node that contains this list head. /// @return Pointer to the end node (the node after the last node) //---------------------------------------------------------------------------------------- NODE* End() const { // The BaseListLinkPOD might be somewhere in the NODE. Since offsetof() is a nogo and // NODE might contain virtual stuff we have to get this offset the hard way. Since // some compilers try to be mighty clever the dummy pointer must not be 0. static const NODE* dummy = (NODE*) sizeof(void*); static const Int offset = Int(dummy->_GetLink()) - Int(dummy); return (NODE*) (Int(this) - offset); } }; //---------------------------------------------------------------------------------------- /// Basic list template (double linked). /// /// The BaseList implements the same methods as the arrays. Nonetheless it is highly /// recommended to use the iterator based methods instead of those taking an index /// or count as paramter because the latter perform poorly due to the nature of a list. /// /// If you want to control the list nodes themselves or their memory layout you can specify /// the list node type with NODE instead of using the default template BaseListNode /// (the same goes for the list head HEAD). /// /// @tparam T Type of the list element content. /// @tparam NODE A node that encapsulates the element content T and holds the pointers to the next and previous element (see BaseListNode for details). /// @tparam HEAD A list head for nodes of type NODE. /// @tparam ALLOCATOR Class for memory allocation. //---------------------------------------------------------------------------------------- template, typename HEAD = BaseListHead, typename ALLOCATOR = DefaultAllocator> class BaseList : protected ALLOCATOR { DISALLOW_COPY_AND_ASSIGN(BaseList); public: typedef T ValueType; typedef ALLOCATOR AllocatorType; template class IteratorTemplate; typedef IteratorTemplate Iterator; typedef IteratorTemplate ConstIterator; BaseList() { } /// this constructor has to be used if a list should use a custom allocator with member variables explicit BaseList(const ALLOCATOR& a) : ALLOCATOR(a) { } ~BaseList() { Reset(); } /// move constructor BaseList(BaseList&& src) : ALLOCATOR(std::move(src)), _head(std::move(src._head)) { } /// move assignment operator MOVE_ASSIGNMENT_OPERATOR(BaseList) //---------------------------------------------------------------------------------------- /// Deletes all elements (calls destructors and frees memory) //---------------------------------------------------------------------------------------- void Reset() { for (Iterator e = End(); e != Begin(); e = End()) Erase(--e); } //---------------------------------------------------------------------------------------- /// Gets the number of list elements. /// This has to iterate over all list elements. You may better want to use iterators directly! /// @return Number of list elements. //---------------------------------------------------------------------------------------- Int GetCount() const { return _head.GetCount(); } //---------------------------------------------------------------------------------------- /// Array (subscript) operator for const objects. /// This is very ineffective on a list - better use iterators! /// @param[in] idx Element index (if it's out of bounds you will get an error in debug code only, otherwise it will crash) /// @return List element. //---------------------------------------------------------------------------------------- const T& operator [](Int idx) const { return *_head.GetElement(idx); } //---------------------------------------------------------------------------------------- /// Array (subscript) operator for non-const objects. /// This is very ineffective on a list - better use iterators! /// @param[in] idx Element index (if it's out of bounds you will get an error in debug code only, otherwise it will crash) /// @return List element. //---------------------------------------------------------------------------------------- // this is duplicate code but casting constness away for this case is plain ugly T& operator [](Int idx) { return *_head.GetElement(idx); } //---------------------------------------------------------------------------------------- /// Adds a new element at the end of the list. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Append() { NODE* node = new (AllocNode()) NODE(); return node ? InsertNode(End(), node) : nullptr; } //---------------------------------------------------------------------------------------- /// Adds a new element at the end of the list and initializes it with a copy of x. /// @param[in] x Value to be copied. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Append(const T& x) { NODE* node = (NODE*) AllocNode(); if (TestForCopyFromMember::isSupported) { new (node) NODE(); if (AssignCopy(*node->_GetData(), x) == false) // copy failed? { DeleteNode(node); node = nullptr; } } else // use trivial copy constructor new (node) NODE(x); return node ? InsertNode(End(), node) : nullptr; } //---------------------------------------------------------------------------------------- /// Adds a new element at the end of the list and moves the content of x to it. /// @param[in] x Value to be moved. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Append(T&& x) { NODE* node = new (AllocNode()) NODE(std::move(x)); return node ? InsertNode(End(), node) : nullptr; } //---------------------------------------------------------------------------------------- /// BaseList specific: Adds a new list node at the end of the list. /// @param[in] node Pointer to new list node (BaseList will take ownership of it) /// @return Element pointer or nullptr (in this case x is still valid) //---------------------------------------------------------------------------------------- T* AppendNode(NODE* node) { return InsertNode(End(), node); } //---------------------------------------------------------------------------------------- /// Inserts a new default element at index position. Use the iterator based Insert() below! /// This is compatible to the arrays but slow because Insert() has to iterate over the list /// to find the element corresponding to the index position. /// @param[in] position Insert index. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Insert(Int position) { Iterator it(_head.GetNode(position)); return it.IsValid() ? Insert(it).GetPtr() : nullptr; } //---------------------------------------------------------------------------------------- /// Inserts a new default element at iterator position. /// @param[in] position Insert position. /// @return Iterator for the new element (IsValid() == false if allocation failed) //---------------------------------------------------------------------------------------- Iterator Insert(Iterator position) { NODE* node = new (AllocNode()) NODE(); if (node) InsertNode(position, node); return Iterator(node); } //---------------------------------------------------------------------------------------- /// Inserts a new element at index position and initializes it with a copy of x. Use the iterator based Insert() below! /// This is compatible to the arrays but slow because Insert() has to iterate over the list. /// @param[in] position Insert index. /// @param[in] x Value to be copied. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Insert(Int position, const T& x) { Iterator it(_head.GetNode(position)); return it.IsValid() ? Insert(it, x).GetPtr() : nullptr; } //---------------------------------------------------------------------------------------- /// Inserts a new element at iterator position and initializes it with a copy of x. /// @param[in] position Insert position. /// @param[in] x Value to be copied. /// @return Iterator for the new element (IsValid() == false if allocation failed) //---------------------------------------------------------------------------------------- Iterator Insert(Iterator position, const T& x) { NODE* node = (NODE*) AllocNode(); if (TestForCopyFromMember::isSupported) { new (node) NODE(); if (AssignCopy(*node->_GetData(), x) == false) // copy failed? { DeleteNode(node); node = nullptr; } } else // use trivial copy constructor new (node) NODE(x); if (node) InsertNode(position, node); return Iterator(node); } //---------------------------------------------------------------------------------------- /// Inserts a new element at index position and moves the content of x to it. Use the iterator based Insert() below! /// This is compatible to the arrays but slow because Insert() has to iterate over the list. /// @param[in] position Insert index. /// @param[in] x Value to be moved. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Insert(Int position, T&& x) { Iterator it(_head.GetNode(position)); return it.IsValid() ? Insert(it, std::move(x)).GetPtr() : nullptr; } //---------------------------------------------------------------------------------------- /// Inserts a new element at iterator position and moves the content of x to it. /// @param[in] position Insert position. /// @param[in] x Value to be moved. /// @return Iterator for the new element (IsValid() == false if allocation failed) //---------------------------------------------------------------------------------------- Iterator Insert(Iterator position, T&& x) { NODE* node = new (AllocNode()) NODE(std::move(x)); if (node) InsertNode(position, node); return Iterator(node); } //---------------------------------------------------------------------------------------- /// BaseList specific: Inserts a new list node at iterator position. /// @param[in] position Insert position. /// @param[in] node Pointer to new list node (BaseList will take ownership of it) /// @return Iterator for the new element (IsValid() == false if something failed, in this case x is still valid) //---------------------------------------------------------------------------------------- T* InsertNode(Iterator position, NODE* node) { node->InsertBefore(position.GetNode()); return node->_GetData(); } //---------------------------------------------------------------------------------------- /// Inserts new elements at index position. Use the iterator based Insert() below! /// This is compatible to the arrays but slow because Insert() has to iterate over the list. /// @param[in] position Insert index. /// @param[in] x Array with values to be copied or nullptr (in this case you have to call the constructor manually) /// @param[in] insertCnt Number of elements to be inserted. /// @return Element pointer or nullptr (allocation failed) //---------------------------------------------------------------------------------------- T* Insert(Int position, const T* x, Int insertCnt) { NODE* node = _head.GetNode(position); return node ? Insert(Iterator(node), x, insertCnt).GetPtr() : nullptr; } //---------------------------------------------------------------------------------------- /// Inserts new elements at iterator position (all elements from position on are moved by insertCnt) /// @param[in] position Insert position. /// @param[in] x List with values to be copied or nullptr (in this case you have to call the constructor manually) /// @param[in] insertCnt Number of elements to be inserted. /// @return Iterator for the new element (IsValid() == false if allocation failed) //---------------------------------------------------------------------------------------- Iterator Insert(Iterator position, const T* x, Int insertCnt) { for (Int i = insertCnt - 1; i >= 0; i--) { Iterator next = position; if (x) position = Insert(next, x[i]); else position = Insert(next); if (position.IsValid() == false) // insertion failed (not enough memory)? { for (i = insertCnt - 1 - i; i > 0; i--) next = Erase(next); // remove the already inserted elements DebugStop(); break; } } return position; } //---------------------------------------------------------------------------------------- /// Erases (removes and deletes) elements. Use the iterator based Erase() below! /// This is compatible to the arrays but slow because Erase() has to iterate over the list. /// @param[in] position Index (Erase() will fail if out of bounds and return nullptr) /// @param[in] eraseCnt Number of elements to be erased (if eraseCnt is higher than what is available at position Erase() will succeed, but remove only the number of available elements) /// @return Pointer to the element that is now at position or nullptr (no more element at position, either because position is out of bounds or the last element was erased) //---------------------------------------------------------------------------------------- T* Erase(Int position, Int eraseCnt = 1) { NODE* node = _head.GetNode(position); T* element = node ? Erase(Iterator(node), eraseCnt).GetPtr() : nullptr; return (element != End().GetPtr()) ? element : nullptr; } //---------------------------------------------------------------------------------------- /// Erases (removes and deletes) elements. /// @param[in] position Erase position. /// @param[in] eraseCnt Number of elements to be erased (if eraseCnt is higher than what is available at position Erase() will succeed, but remove only the number of available elements) /// @return Iterator for the element that is now at position (IsValid() == false if something failed) //---------------------------------------------------------------------------------------- Iterator Erase(Iterator position, Int eraseCnt) { for (Int i = 0; i < eraseCnt; i++) { if (position == End()) { position = Iterator(); // set invalid iterator to signal an error DebugStop(); break; } position = Erase(position); } return position; } Iterator Erase(Iterator position) { NODE* node = position.GetNode(); position++; DeleteNode(node); return position; } //---------------------------------------------------------------------------------------- /// Returns the first element of the list. /// @return Pointer to the first element (null if the list is empty) //---------------------------------------------------------------------------------------- const T* GetFirst() const { return _head.GetFirst(); } //---------------------------------------------------------------------------------------- /// Returns the first element of the list. /// @return Pointer to the first element (null if the list is empty) //---------------------------------------------------------------------------------------- T* GetFirst() { return _head.GetFirst(); } //---------------------------------------------------------------------------------------- /// Gets the last element. /// @return Last element or nullptr. //---------------------------------------------------------------------------------------- const T* GetLast() const { return _head.GetLast(); } //---------------------------------------------------------------------------------------- /// Gets the last element. /// @return Last element or nullptr. //---------------------------------------------------------------------------------------- T* GetLast() { return _head.GetLast(); } //---------------------------------------------------------------------------------------- /// Resizes the list to contain newCnt elements /// If newCnt is smaller than GetCount() all extra elements are being deleted. If it is /// greater the list is expanded and the default constructor is called for new elements. /// @param[in] newCnt New list size. /// @return False if allocation failed. //---------------------------------------------------------------------------------------- Bool Resize(Int newCnt) { Int cnt = GetCount(); Int increment = newCnt - cnt; Bool success = false; if (increment <= 0) // decrease list size? { if (newCnt >= 0) { for (Iterator it = --End(); increment != 0; increment++) Erase(it--); success = true; } else DebugStop(); } else // increase list size { for (; increment > 0; increment--) { if (Append() == nullptr) break; } success = increment == 0; } return success; } //---------------------------------------------------------------------------------------- /// Deletes the last element. /// @param[out] dst Nullptr or pointer to return value. /// @return True if successful. //---------------------------------------------------------------------------------------- Bool Pop(T* dst = nullptr) { Iterator it = End(); if (it != Begin()) { --it; if (dst) *dst = std::move(*it); // call move constructor if available Erase(it); return true; } return false; } //---------------------------------------------------------------------------------------- /// BaseList specific: Removes the last node and returns the pointer to it. /// @param[out] dst Used to return pointer to the last node (must not be null), the caller will take ownership of the node. /// @return True if successful. //---------------------------------------------------------------------------------------- Bool PopNode(NODE** dst) { Iterator it = End(); if (it != Begin()) { --it; it.GetNode()->Remove(); *dst = it.GetNode(); return true; } *dst = nullptr; return false; } //---------------------------------------------------------------------------------------- /// Gets the index of element. The element must be part of the list, otherwise (e.g. if x is /// a copy of a list element) InvalidArrayIndex will be returned. /// This is compatible to the arrays but slow because GetIndex() has to iterate over the list. /// @return Index of element or InvalidArrayIndex (not element of this) //---------------------------------------------------------------------------------------- Int GetIndex(const T& x) const { return _head.GetIndex(&x); } //---------------------------------------------------------------------------------------- /// BaseList specific: Removes the node and returns a pointer to it. /// @param[in] position Position of the element to be removed. /// @return Node pointer or null, the caller will take ownership of it. //---------------------------------------------------------------------------------------- static NODE* RemoveNode(Iterator position) { NODE* node = nullptr; if (position.IsValid()) { node = position.GetNode(); node->Remove(); } return node; } //---------------------------------------------------------------------------------------- /// BaseList specific: Removes the node and returns a pointer to it. /// @param[in] x The element to be removed. /// @return Node pointer or null, the caller will take ownership of it. //---------------------------------------------------------------------------------------- static NODE* RemoveNode(T* x) { return RemoveNode(Iterator(x)); } //---------------------------------------------------------------------------------------- /// Swaps elements a and b (just changes the pointers, more efficient than global Swap(list[a], list[b]) /// @param[in] a Position of element to be swapped. /// @param[in] b Position of element to be swapped. //---------------------------------------------------------------------------------------- void Swap(Iterator a, Iterator b) { NODE* aNode = a.GetNode(); NODE* bNode = b.GetNode(); NODE* aNext = aNode->_GetNext(); NODE* bNext = bNode->_GetNext(); if (aNext == bNode) // b is directly behind a? { bNode->Remove(); bNode->InsertBefore(aNode); } else if (bNext == aNode) // a is directly behind b? { aNode->Remove(); aNode->InsertBefore(bNode); } else // a and b are not directly linked { aNode->Remove(); bNode->Remove(); bNode->InsertBefore(aNext); aNode->InsertBefore(bNext); } } //---------------------------------------------------------------------------------------- /// Gets an iterator for the first element /// When you modify the list Begin() will change, it is not a constant value. /// @return Iterator for the first element (equal to End() if the list is empty) //---------------------------------------------------------------------------------------- ConstIterator Begin() const { return ConstIterator(_head.Begin()); } //---------------------------------------------------------------------------------------- /// Gets an iterator for the first element /// When you modify the list Begin() will change, it is not a constant value. /// @return Iterator for the first element (equal to End() if the list is empty) //---------------------------------------------------------------------------------------- Iterator Begin() { return Iterator(_head.Begin()); } //---------------------------------------------------------------------------------------- /// Gets an iterator for the end (End() - 1 is the last element if the list is not empty) /// For the BaseList End() is in fact a constant value, it won't change even if you insert /// or remove elements. That's different from all arrays (where End() will change when you /// modify the array). /// @return Iterator for the list end (this is behind the last element) //---------------------------------------------------------------------------------------- ConstIterator End() const { return ConstIterator(_head.End()); } //---------------------------------------------------------------------------------------- /// Gets an iterator for the end (End() - 1 is the last element if the list is not empty) /// For the BaseList End() is in fact a constant value, it won't change even if you insert /// or remove elements. That's different from all arrays (where End() will change when you /// modify the array). /// @return Iterator for the list end (this is behind the last element) //---------------------------------------------------------------------------------------- Iterator End() { return Iterator(_head.End()); } //---------------------------------------------------------------------------------------- /// The BaseList iterator internally is a pointer to the NODE containing the data of /// type T and using it to iterate over a list or parts of it is as efficient as using a /// real pointer to the list nodes (for more ease of use you may want to invoke this via AutoIterator). /// /// As already said you can use an Iterator almost like a pointer, e.g. /// @code /// it++; // go to the next element /// it--; // go to the previous element /// it += 5; // advance by 5 elements /// it -= 3; // go back 3 elements /// cnt = itB - itA; // number of elements from itA to itB /// it = list.Begin(); // iterator to the first element of the list /// *it = value; // assign value to the elements referenced by the iterator /// value = *it; // get value of the element referenced by the iterator /// @endcode /// /// Please note that using a postfix operator access (*it++ or *it--) can be slower than /// using the prefix form or a separate assignment. E.g. /// @code value = *it++; @endcode is most likely slower than /// @code value = it; ++it; @endcode or @code value = it; it++; @endcode /// because *it++ requires a temporary copy of the iterator that the compiler may not /// be able to remove during optimization. As long as you only use the iterator's postfix /// operator without assignment it should be fine because the compiler will remove the /// temporary copy. //---------------------------------------------------------------------------------------- template class IteratorTemplate { public: // For a const iterator, the BaseList, its values and its nodes have to be const within the iterator, otherwise they are non-const. // These typedefs have to be used throughout the iterator code instead of just BaseList, NODE or T. typedef typename ConstIf::Type CollectionType; typedef typename ConstIf::Type ValueType; typedef typename ConstIf::Type NodeType; static const Bool isLinearIterator = false; explicit IteratorTemplate(CollectionType& l) : _node(l._head.Begin()) { } // NODE can be the same as T, therefore just one constructor for the iterator explicit IteratorTemplate(NodeType* pos = nullptr) : _node(pos) { } IteratorTemplate(const IteratorTemplate& src) : _node(src._node) { } IteratorTemplate& operator =(const IteratorTemplate& src) { _node = src._node; // self assignment is no problem here, therefore no check if (this != &src) return *this; } #ifdef __INTEL_COMPILER #pragma warning disable 597 #endif operator ConstIterator&() { return *(ConstIterator*) this; } #ifdef __INTEL_COMPILER #pragma warning enable 597 #endif ValueType* GetPtr() const { return _node->_GetData(); } //---------------------------------------------------------------------------------------- /// @return true if the iterator points to an element (Iterator().IsValid() will return false) //---------------------------------------------------------------------------------------- Bool IsValid() const { return _node != nullptr; } ValueType& operator *() const { return *_node->_GetData(); } ValueType* operator ->() const { return _node->_GetData(); } Bool operator ==(const IteratorTemplate& b) const { return _node == b._node; } Bool operator !=(const IteratorTemplate& b) const { return _node != b._node; } IteratorTemplate& operator ++() // prefix operator ++ (increment and fetch) { _node = _node->_GetNext(); return *this; } const IteratorTemplate operator ++(int) // postfix operator ++ (fetch and increment) { NodeType* tmp = _node; _node = _node->_GetNext(); return IteratorTemplate(tmp); // use RVO } IteratorTemplate& operator --() // prefix operator -- (decrement and fetch) { _node = _node->_GetPrev(); return *this; } const IteratorTemplate operator --(int) // postfix operator -- (fetch and decrement) { NodeType* tmp = _node; _node = _node->_GetPrev(); return IteratorTemplate(tmp); // use RVO } IteratorTemplate operator +(Int i) const // operator + { NodeType* tmp = _node; for (; i > 0; i--) tmp = tmp->_GetNext(); return IteratorTemplate(tmp); // use RVO } IteratorTemplate operator -(Int i) const { NodeType* tmp = _node; for (; i > 0; i--) tmp = tmp->_GetPrev(); return IteratorTemplate(tmp); // use RVO } NodeType* GetNode() const { return _node; } private: NodeType* _node; }; //---------------------------------------------------------------------------------------- /// Returns the allocator as reference. Typically this is used by the arrays and other /// base classes when multiple of them are "stiched" together as one big object all /// shall use one main allocator. /// @return Allocator reference. //---------------------------------------------------------------------------------------- ALLOCATOR& GetAllocator() { return *this; } private: void* AllocNode() { return this->Alloc(SIZEOF(NODE), C4D_MISC_ALLOC_LOCATION); } void DeleteNode(NODE* node) { node->~NODE(); // will implicitely call Remove() this->Free(node); } HEAD _head; }; /// This supports c4d style list code that iterates through a list with GetNext()/GetPrev() and stops when null is returned template class BaseListLegacyNode { typedef BaseListNode NODE; public: /// Returns the pointer to the next element or null (this is the last element of the list) T* GetNext() const { // The data of type T might be somewhere in the NODE. Since offsetof() is a nogo and // NODE might contain virtual stuff we have to get this offset the hard way. Since // some compilers try to be mighty clever the dummy pointer must not be 0. static const NODE* dummy = (NODE*) sizeof(void*); static const Int offset = Int(dummy->_GetData()) - Int(dummy); NODE* node = (NODE*) (Int(this) - offset); NODE* next = node->_GetNext(); return next->IsListHead() ? nullptr : (T*) (Int(next) + offset); } /// Returns the pointer to the previous element or null (this is the first element of the list) T* GetPrev() const { // The data of type T might be somewhere in the NODE. Since offsetof() is a nogo and // NODE might contain virtual stuff we have to get this offset the hard way. Since // some compilers try to be mighty clever the dummy pointer must not be 0. static const NODE* dummy = (NODE*) sizeof(void*); static const Int offset = Int(dummy->_GetData()) - Int(dummy); NODE* node = (NODE*) (Int(this) - offset); NODE* prev = node->_GetPrev(); return prev->IsListHead() ? nullptr : (T*) (Int(prev) + offset); } }; /// Use this macro to implement custom BaseListNodes where T and NODE are equal. /// NODE is the name of the class and linkPtr is the address of the BaseListLinkPOD. /// Don't forget that your custom node must have a destructor which has to call this->Remove(). #define IMPLEMENT_CUSTOM_BASELISTNODE(NODE, linkPtr) \ friend class BaseList; \ typedef BaseListLink Link; \ void Remove() { Link::Remove(this); } \ void InsertBefore(NODE* next) { Link::InsertBefore(this, next); } \ NODE* _GetNext() const { return ((Link*)linkPtr)->_GetNext(); } \ NODE* _GetPrev() const { return ((Link*)linkPtr)->_GetPrev(); } \ void SetNext(NODE* val) { ((Link*)linkPtr)->SetNext(val); } \ void SetPrev(NODE* val) { ((Link*)linkPtr)->SetPrev(val); } \ Bool IsListHead() const { return ((Link*)linkPtr)->IsListHead(); } \ NODE* _GetData() const { return (NODE*) this; } \ BaseListLinkPOD* _GetLink() const { return (Link*) linkPtr; } /// @} } // C4D_MISC_END #endif // BASELIST_H__