Sun, 05 Mar 2017 16:47:52 +0200
Moved LDObject lifetime management from Model to ResourceVector. This is a large refactor that removes some hacks from the Model class.
/* * LDForge: LDraw parts authoring CAD * Copyright (C) 2013 - 2017 Teemu Piippo * * 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 <http://www.gnu.org/licenses/>. */ #pragma once #include <QVector> /* * A vector that allocates elements on the heap and manages their lifetime. */ template<typename T> class ResourceVector { public: using Filter = std::function<bool(T*, int)>; using Callback = std::function<void(T*, int)>; ~ResourceVector(); void append(T) = delete; template<typename TT = T, typename... Args> TT* append(Args&&... args); void assimilate(int position, T* resource); T* const* begin() const; T* const* end() const; int indexOf(T* resource) const; template<typename TT = T, typename... Args> TT* insert(int position, Args&&... args); bool isEmpty() const; void clear(); void removeAt(int position); T* operator[](int position) const; int size() const; void swap(int position1, int position2); void merge(ResourceVector<T>& other, int position = -1, Filter filter = nullptr, Callback callback = nullptr); const QVector<T*>& toQVector() const; private: QVector<T*> _data; }; template<typename T> int countof(const ResourceVector<T>& vector); /* * Frees the resources when the vector is deleted. */ template<typename T> ResourceVector<T>::~ResourceVector() { clear(); } /* * Creates a new resource and adds it to the end of this vector. */ template<typename T> template<typename TT, typename... Args> TT* ResourceVector<T>::append(Args&&... args) { TT* resource = new TT {args...}; _data.append(resource); return resource; } /* * Returns an iterator to the beginning of this vector. */ template<typename T> T* const* ResourceVector<T>::begin() const { return _data.cbegin(); } /* * Returns an iterator to the end of this vector. */ template<typename T> T* const* ResourceVector<T>::end() const { return _data.cend(); } /* * Creates a new resource and inserts it to the specified position in this vector. */ template<typename T> template<typename TT, typename... Args> TT* ResourceVector<T>::insert(int position, Args&&... args) { TT* resource = new TT {args...}; _data.insert(position, resource); return resource; } /* * Deletes all resources in this vector. */ template<typename T> void ResourceVector<T>::clear() { for (T* element : _data) delete element; _data.clear(); } /* * Removes the resource at the provided position. Throws an exception if the index is out of range. */ template<typename T> void ResourceVector<T>::removeAt(int position) { if (position >= 0 and position < _data.size()) { delete _data[position]; _data.removeAt(position); } else { throw std::domain_error {"index out of range"}; } } /* * Returns the resource at the provided position. Throws an exception if the index is out of range. */ template<typename T> T* ResourceVector<T>::operator[](int position) const { if (position >= 0 and position < _data.size()) return _data[position]; else throw std::domain_error {"index out of range"}; } /* * Returns the amount of resources in this vector. */ template<typename T> int ResourceVector<T>::size() const { return _data.size(); } /* * Inserts an already existing resource into the vector. The resource is assumed to not be managed by anything else! * Please think twice before using this method. */ template<typename T> void ResourceVector<T>::assimilate(int position, T* resource) { _data.insert(position, resource); } /* * Returns the index of the provided resource in the vector, or -1 if not found. * This operation is of linear complexity O(n). */ template<typename T> int ResourceVector<T>::indexOf(T* resource) const { return _data.indexOf(resource); } /* * Swaps the locations of two resource in this vector. */ template<typename T> void ResourceVector<T>::swap(int position1, int position2) { if (position1 >= 0 and position2 >= 0 and position1 < size() and position2 < size()) { qSwap(_data[position1], _data[position2]); } else { throw std::domain_error {"index out of range"}; } } /* * Returns whether or not the vector is empty. */ template<typename T> bool ResourceVector<T>::isEmpty() const { return size() == 0; } /* * Migrates resources from the provided resource vector to this one. * - `position` indicates where to insert the new resources. Default is to the end. * - `filter` specifies a filter function for the migration. Objects not passing the filter will not be migrated. * - `callback` specifies a function that is called for each successfully migrated object. */ template<typename T> void ResourceVector<T>::merge(ResourceVector<T>& other, int position, Filter filter, Callback callback) { if (position < 0) position = size(); if (filter == nullptr) filter = [](T*, int){return true;}; if (callback == nullptr) callback = [](T*, int){}; for (int i = 0; i < other.size();) { T* resource = other[i]; if (filter(resource, i)) { _data.insert(position, resource); other._data.removeAt(i); callback(resource, position); position += 1; } else { i += 1; } } } /* * Returns the underlying vector. */ template<typename T> const QVector<T*>& ResourceVector<T>::toQVector() const { return _data; } /* * countof() overload for resource vectors. Returns the amount of resources in the given vector. */ template<typename T> int countof(const ResourceVector<T>& vector) { return vector.size(); }