src/types/resourcevector.h

Sun, 05 Mar 2017 16:47:52 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Sun, 05 Mar 2017 16:47:52 +0200
changeset 1186
eae8b3bce545
parent 1185
c2e0db52ea07
child 1189
0509b2b5eaa6
permissions
-rw-r--r--

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();
}

mercurial