generic type-erased container types CSequence and CCollection, including predicate-based algorithms

refs #81
This commit is contained in:
Mathew Sutcliffe
2013-12-10 16:09:37 +00:00
parent c72e8a4a27
commit a57e640398
15 changed files with 1905 additions and 3 deletions

View File

@@ -1621,7 +1621,7 @@ INCLUDE_FILE_PATTERNS =
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
PREDEFINED =
PREDEFINED := Q_COMPILER_VARIADIC_TEMPLATES
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.

View File

@@ -1621,7 +1621,7 @@ INCLUDE_FILE_PATTERNS =
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
PREDEFINED =
PREDEFINED := Q_COMPILER_VARIADIC_TEMPLATES
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.

View File

@@ -34,4 +34,19 @@
* \brief Classes for physical quantities and units such as length, mass, speed.
*/
/*!
* \namespace BlackMisc::Predicates
* \brief Functor classes for evaluating predicate calculus expressions.
*/
/*!
* \internal
* \namespace BlackMisc::Predicates::Private
*/
/*!
* \namespace BlackMisc::Iterators
* \brief Iterator classes for the containers.
*/
#endif

View File

@@ -6,6 +6,7 @@
#ifndef BLACKMISC_FREEFUNCTIONS_H
#define BLACKMISC_FREEFUNCTIONS_H
#include "valueobject.h" // for qHash overload
#include <QDir> // for Q_INIT_RESOURCE
#include <QList>
#include <QVariant>

291
src/blackmisc/collection.h Normal file
View File

@@ -0,0 +1,291 @@
/* Copyright (C) 2013 VATSIM Community / authors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*!
\file
*/
#ifndef BLACKMISC_COLLECTION_H
#define BLACKMISC_COLLECTION_H
#include "iterator.h"
#include "containerbase.h"
#include <QScopedPointer>
#include <algorithm>
#include <type_traits>
#include <iterator>
#include <utility>
namespace BlackMisc
{
/*!
* \brief Generic type-erased unsequenced container with value semantics.
* \tparam T the type of elements contained.
*
* Can take any suitable container class as its implementation at runtime.
*/
template <class T>
class CCollection : public CContainerBase<CCollection, T>
{
public:
//! \brief STL compatibility
//! @{
typedef T key_type;
typedef T value_type;
typedef T &reference;
typedef const T &const_reference;
typedef T *pointer;
typedef const T *const_pointer;
typedef typename Iterators::ConstForwardIterator<T> const_iterator;
typedef const_iterator iterator; // can't modify elements in-place
typedef ptrdiff_t difference_type;
typedef int size_type;
//! @}
/*!
* \brief Default constructor.
*/
CCollection() : m_pimpl(new Pimpl<QSet<T>>(QSet<T>())) {}
/*!
* \brief Constructor.
* \tparam C Becomes the collection's implementation type.
* \param c Initial value for the collection; typically empty, but could contain elements.
*/
template <class C> CCollection(C c) : m_pimpl(new Pimpl<C>(std::move(c))) {}
/*!
* \brief Copy constructor.
* \param other
*/
CCollection(const CCollection &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {}
/*!
* \brief Move constructor.
* \param other
*/
CCollection(CCollection &&other) : m_pimpl(other.m_pimpl.take()) {}
/*!
* \brief Assignment.
* \tparam C Becomes the collection's new implementation type.
* \param c New value for the collection; typically empty, but could contain elements.
*/
template <class C> CCollection &operator =(C c) { m_pimpl.reset(new Pimpl<C>(std::move(c))); return *this; }
/*!
* \brief Copy assignment.
* \param other
* \return
*/
CCollection &operator =(const CCollection &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; }
/*!
* \brief Move assignment.
* \param other
* \return
*/
CCollection &operator =(CCollection && other) { m_pimpl.reset(other.m_pimpl.take()); return *this; }
/*!
* \brief Change the implementation type but keep all the same elements, by copying them into the new implementation.
* \tparam C Becomes the collection's new implementation type.
*/
template <class C> void changeImpl(C = C()) { CCollection c = C(); for (auto i = cbegin(); i != cend(); ++i) c.insert(*i); *this = std::move(c); }
/*!
* \brief Like changeImpl, but uses the implementation type of another collection.
* \param other
* \pre The other collection must be initialized.
*/
void useImplOf(const CCollection &other) { PimplPtr p = other.pimpl()->cloneEmpty(); for (auto i = cbegin(); i != cend(); ++i) p->insert(*i); m_pimpl.reset(p.take()); }
/*!
* \brief Returns iterator at the beginning of the collection.
* \return
*/
iterator begin() { return pimpl() ? pimpl()->begin() : iterator(); }
/*!
* \brief Returns iterator at the beginning of the collection.
* \return
*/
const_iterator begin() const { return pimpl() ? pimpl()->begin() : const_iterator(); }
/*!
* \brief Returns iterator at the beginning of the collection.
* \return
*/
const_iterator cbegin() const { return pimpl() ? pimpl()->cbegin() : const_iterator(); }
/*!
* \brief Returns iterator one past the end of the collection.
* \return
*/
iterator end() { return pimpl() ? pimpl()->end() : iterator(); }
/*!
* \brief Returns iterator one past the end of the collection.
* \return
*/
const_iterator end() const { return pimpl() ? pimpl()->end() : const_iterator(); }
/*!
* \brief Returns iterator one past the end of the collection.
* \return
*/
const_iterator cend() const { return pimpl() ? pimpl()->cend() : const_iterator(); }
/*!
* \brief Swap this collection with another.
* \param other
*/
void swap(CCollection &other) { m_pimpl.swap(other.m_pimpl); }
/*!
* \brief Returns number of elements in the collection.
* \return
*/
size_type size() const { return pimpl() ? pimpl()->size() : 0; }
/*!
* \brief Returns true if the collection is empty.
* \return
*/
bool empty() const { return pimpl() ? pimpl()->empty() : true; }
/*!
* \brief Synonym for empty.
* \return
*/
bool isEmpty() const { return empty(); }
/*!
* \brief Removes all elements in the collection.
*/
void clear() { if (pimpl()) pimpl()->clear(); }
/*!
* \brief Inserts an element into the collection.
* \param value
* \return An iterator to the position where value was inserted.
* \pre The collection must be initialized.
*/
iterator insert(const T &value) { Q_ASSERT(pimpl()); return pimpl()->insert(value); }
/*!
* \brief Synonym for insert.
* \param value
* \return An iterator to the position where value was inserted.
* \pre The collection must be initialized.
*/
iterator push_back(const T &value) { return insert(value); }
/*!
* \brief Remove the element pointed to by the given iterator.
* \param pos
* \return An iterator to the position of the next element after the one removed.
* \pre The collection must be initialized.
*/
iterator erase(iterator pos) { Q_ASSERT(pimpl()); return pimpl()->erase(pos); }
/*!
* \brief Remove the range of elements between two iterators.
* \param it1
* \param it2
* \return An iterator to the position of the next element after the one removed.
* \pre The sequence must be initialized.
*/
iterator erase(iterator it1, iterator it2) { Q_ASSERT(pimpl()); return pimpl()->erase(it1, it2); }
/*!
* \brief Test for equality.
* \param other
* \return
* \todo Improve inefficient implementation.
*/
bool operator ==(const CCollection &other) const { return (empty() && other.empty()) ? true : (size() != other.size() ? false : *pimpl() == *other.pimpl()); }
/*!
* \brief Test for inequality.
* \param other
* \return
* \todo Improve inefficient implementation.
*/
bool operator !=(const CCollection &other) const { return !(*this == other); }
private:
class PimplBase
{
public:
virtual ~PimplBase() {}
virtual PimplBase *clone() const = 0;
virtual PimplBase *cloneEmpty() const = 0;
virtual iterator begin() = 0;
virtual const_iterator begin() const = 0;
virtual const_iterator cbegin() const = 0;
virtual iterator end() = 0;
virtual const_iterator end() const = 0;
virtual const_iterator cend() const = 0;
virtual size_type size() const = 0;
virtual bool empty() const = 0;
virtual void clear() = 0;
virtual iterator insert(const T &value) = 0;
virtual iterator erase(iterator pos) = 0;
virtual iterator erase(iterator it1, iterator it2) = 0;
virtual bool operator ==(const PimplBase &other) const = 0;
protected:
// using SFINAE to choose whether to implement insert() in terms of either push_back() or insert(), depending on which is available
// https://groups.google.com/forum/#!original/comp.lang.c++.moderated/T3x6lvmvvkQ/mfY5VTDJ--UJ
class yes { char x; }; class no { yes x[2]; }; template <class X, X V> struct typecheck {};
struct base { void push_back(); }; template <class C> struct derived : public C, public base {};
static yes hasPushHelper(...); template <class D> static no hasPushHelper(D *, typecheck<void (base::*)(), &D::push_back> * = 0);
template <class C> struct hasPush : public std::integral_constant<bool, sizeof(hasPushHelper((derived<C>*)0)) == sizeof(yes)> {};
template <class C> static iterator insertImpl(typename std::enable_if< hasPush<C>::value, C>::type &c, const T &value) { c.push_back(value); return c.end() - 1; }
template <class C> static iterator insertImpl(typename std::enable_if < !hasPush<C>::value, C >::type &c, const T &value) { return c.insert(value); }
};
template <class C> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename C::value_type>::value, "CCollection must be initialized from a container with the same value_type.");
Pimpl(C &&c) : m_impl(std::move(c)) {}
PimplBase *clone() const { return new Pimpl(*this); }
PimplBase *cloneEmpty() const { return new Pimpl(C()); }
iterator begin() { return m_impl.begin(); }
const_iterator begin() const { return m_impl.cbegin(); }
const_iterator cbegin() const { return m_impl.cbegin(); }
iterator end() { return m_impl.end(); }
const_iterator end() const { return m_impl.cend(); }
const_iterator cend() const { return m_impl.cend(); }
size_type size() const { return m_impl.size(); }
bool empty() const { return m_impl.empty(); }
void clear() { m_impl.clear(); }
iterator insert(const T &value) { return PimplBase::insertImpl<C>(m_impl, value); }
iterator erase(iterator pos) { return m_impl.erase(*static_cast<const typename C::iterator *>(pos.getImpl())); }
iterator erase(iterator it1, iterator it2) { return m_impl.erase(*static_cast<const typename C::iterator *>(it1.getImpl(), it2.getImpl())); }
bool operator ==(const PimplBase &other) const { Pimpl copy = C(); for (auto i = other.cbegin(); i != other.cend(); ++i) copy.insert(*i); return m_impl == copy.m_impl; }
private:
C m_impl;
};
typedef QScopedPointer<PimplBase> PimplPtr;
PimplPtr m_pimpl;
// using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const
PimplBase *pimpl() { return m_pimpl.data(); }
const PimplBase *pimpl() const { return m_pimpl.data(); }
};
} //namespace BlackMisc
Q_DECLARE_METATYPE(BlackMisc::CCollection<int>)
Q_DECLARE_METATYPE(BlackMisc::CCollection<uint>)
Q_DECLARE_METATYPE(BlackMisc::CCollection<qlonglong>)
Q_DECLARE_METATYPE(BlackMisc::CCollection<qulonglong>)
// CCollection<double> not instantiated because QSet<double> is not supported due to hashing constraints
#endif // guard

View File

@@ -0,0 +1,324 @@
/* Copyright (C) 2013 VATSIM Community / authors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*!
\file
*/
#ifndef BLACKMISC_CONTAINERBASE_H
#define BLACKMISC_CONTAINERBASE_H
#include "valueobject.h"
#include "valuemap.h"
#include "predicates.h"
#include <algorithm>
#define _SCL_SECURE_NO_WARNINGS // suppress MSVC unchecked iterator warning for std::transform
namespace BlackMisc
{
/*!
* \brief Base class for CCollection and CSequence implementing their algorithms.
*/
template <template <class> class C, class T>
class CContainerBase : public CValueObject
{
public:
/*!
* \brief Return a copy containing only those elements for which a given predicate returns true.
* \param p
* \return
*/
template <class Predicate>
C<T> findBy(Predicate p) const
{
C<T> result = derived();
result.erase(std::remove_if(result.begin(), result.end(), [ = ](const T &value) { return !p(value); }), result.end());
return result;
}
/*!
* \brief Return a copy containing only those elements matching a particular key/value pair.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \return
*/
template <class K1, class V1>
C<T> findBy(K1 key1, V1 value1) const
{
return findBy(BlackMisc::Predicates::MemberEqual<T>(key1, value1));
}
/*!
* \brief Return a copy containing only those elements matching some particular key/value pairs.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \param key2 A pointer to a member function of T.
* \param value2 Will be compared to the return value of key2.
* \return
*/
template <class K1, class V1, class K2, class V2>
C<T> findBy(K1 key1, V1 value1, K2 key2, V2 value2) const
{
return findBy(BlackMisc::Predicates::MemberEqual<T>(key1, value1, key2, value2));
}
/*!
* \brief Return a copy containing only those elements matching some particular key/value pairs.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \param key2 A pointer to a member function of T.
* \param value2 Will be compared to the return value of key2.
* \param key3 A pointer to a member function of T.
* \param value3 Will be compared to the return value of key3.
* \return
*/
template <class K1, class V1, class K2, class V2, class K3, class V3>
C<T> findBy(K1 key1, V1 value1, K2 key2, V2 value2, K3 key3, V3 value3) const
{
return findBy(BlackMisc::Predicates::MemberEqual<T>(key1, value1, key2, value2, key3, value3));
}
/*!
* \brief Return a copy containing only those elements matching a given value map.
* \param valueMap
* \return
*/
C<T> findBy(const CValueMap &valueMap) const
{
return findBy([ & ](const T &value) { return value == valueMap; });
}
/*!
* \brief Return true if there is an element for which a given predicate returns true
* \param p
* \return
*/
template <class Predicate>
bool contains(Predicate p) const
{
return std::any_of(derived().begin(), derived().end(), p);
}
/*!
* \brief Return a copy containing only those elements matching a particular key/value pair.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \return
*/
template <class K1, class V1>
bool contains(K1 key1, V1 value1) const
{
return contains(BlackMisc::Predicates::MemberEqual<T>(key1, value1));
}
/*!
* \brief Modify by applying a value map to each element for which a given predicate returns true.
* \pre Requires a sequential container.
* \param p
* \param newValues
*/
template <class Predicate>
void applyIf(Predicate p, const CValueMap &newValues)
{
std::for_each(derived().begin(), derived().end(), [ &, p ](T &value) { if (p(value)) { value.apply(newValues); } });
}
/*!
* \brief Modify by applying a value map to each element matching a particular key/value pair.
* \pre Requires a sequential container.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \param newValues
*/
template <class K1, class V1>
void applyIf(K1 key1, V1 value1, const CValueMap &newValues)
{
applyIf(BlackMisc::Predicates::MemberEqual<T>(key1, value1), newValues);
}
/*!
* \brief Modify by applying a value map to each element matching a given value map.
* \pre Requires a sequential container.
* \param pattern
* \param newValues
*/
void applyIf(const CValueMap &pattern, const CValueMap &newValues)
{
applyIf([ & ](const T &value) { return value == pattern; }, newValues);
}
/*!
* \brief Replace elements for which a given predicate returns true.
* \pre Requires a sequential container.
* \param p
* \param replacement
*/
template <class Predicate>
void replaceIf(Predicate p, const T &replacement)
{
std::replace_if(derived().begin(), derived().end(), p, replacement);
}
/*!
* \brief Replace elements matching a particular key/value pair.
* \pre Requires a sequential container.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \param replacement
*/
template <class K1, class V1>
void replaceIf(K1 key1, V1 value1, const T &replacement)
{
replaceIf(BlackMisc::Predicates::MemberEqual<T>(key1, value1), replacement);
}
/*!
* \brief Replace elements for which a given predicate returns true. If there is no match, push the new element on the end.
* \pre Requires a sequential container.
* \param p
* \param replacement
*/
template <class Predicate>
void replaceOrAdd(Predicate p, const T &replacement)
{
if (contains(p)) { replaceIf(p, replacement); }
else { derived().push_back(replacement); }
}
/*!
* \brief Replace elements matching a particular key/value pair. If there is no match, push the new element on the end.
* \pre Requires a sequential container.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
* \param replacement
*/
template <class K1, class V1>
void replaceOrAdd(K1 key1, V1 value1, const T &replacement)
{
if (contains(key1, value1)) { replaceIf(key1, value1, replacement); }
else { derived().push_back(replacement); }
}
/*!
* \brief Remove elements for which a given predicate returns true.
* \param p
*/
template <class Predicate>
void removeIf(Predicate p)
{
std::remove_if(derived().begin(), derived().end(), p);
}
/*!
* \brief Remove elements matching a particular key/value pair.
* \param key1 A pointer to a member function of T.
* \param value1 Will be compared to the return value of key1.
*/
template <class K1, class V1>
void removeIf(K1 key1, V1 value1)
{
removeIf(BlackMisc::Predicates::MemberEqual<T>(key1, value1));
}
/*!
* \brief Return a copy sorted by a given comparator predicate.
* \pre Requires a sequential container.
* \param p
* \return
*/
template <class Predicate>
C<T> sorted(Predicate p) const
{
C<T> result = derived();
std::sort(result.begin(), result.end(), p);
return result;
}
/*!
* \brief Return a copy sorted by a particular key.
* \pre Requires a sequential container.
* \param key1 A pointer to a member function of T.
* \return
*/
template <class K1>
C<T> sortedBy(K1 key1) const
{
return sorted(BlackMisc::Predicates::MemberLess<T>(key1));
}
/*!
* \brief Return a copy sorted by some particular keys.
* \pre Requires a sequential container.
* \param key1 A pointer to a member function of T.
* \param key2 A pointer to a member function of T.
* \return
*/
template <class K1, class K2>
C<T> sortedBy(K1 key1, K2 key2) const
{
return sorted(BlackMisc::Predicates::MemberLess<T>(key1, key2));
}
/*!
* \brief Return a copy sorted by some particular keys.
* \pre Requires a sequential container.
* \param key1 A pointer to a member function of T.
* \param key2 A pointer to a member function of T.
* \param key3 A pointer to a member function of T.
* \return
*/
template <class K1, class K2, class K3>
C<T> sortedBy(K1 key1, K2 key2, K3 key3) const
{
return sorted(BlackMisc::Predicates::MemberLess<T>(key1, key2, key3));
}
public: // CValueObject overrides
virtual QVariant toQVariant() const { return QVariant::fromValue(derived()); }
// comparing containers by hash will only compare their addresses
virtual uint getValueHash() const { return qHash(&derived()); }
protected: // CValueObject overrides
virtual QString convertToQString(bool i18n = false) const
{
QString str;
// qualifying stringify with this-> to workaround bug in GCC 4.7.2 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56402
std::for_each(derived().begin(), derived().end(), [ & ](const T &value) { str += (str.isEmpty() ? "{" : ",") + this->stringify(value, i18n); });
if (str.isEmpty()) { str = "{"; }
return str += "}";
}
virtual void marshallToDbus(QDBusArgument &argument) const
{
argument.beginArray();
std::for_each(derived().begin(), derived().end(), [ & ](const T &value) { argument << value; });
argument.endArray();
}
virtual void unmarshallFromDbus(const QDBusArgument &argument)
{
argument.beginArray();
while (!argument.atEnd()) { T value; argument >> value; derived().insert(value); }
argument.endArray();
}
private:
C<T> &derived() { return static_cast<C<T> &>(*this); }
const C<T> &derived() const { return static_cast<const C<T> &>(*this); }
template <class U> static QString stringify(const U &obj, bool i18n) { return obj.toQString(i18n); }
static QString stringify(int n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); }
static QString stringify(uint n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); }
static QString stringify(qlonglong n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); }
static QString stringify(qulonglong n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); }
static QString stringify(double n, bool i18n) { return i18n ? QLocale().toString(n) : QString::number(n); }
};
}
#endif // guard

653
src/blackmisc/iterator.h Normal file
View File

@@ -0,0 +1,653 @@
/* Copyright (C) 2013 VATSIM Community / authors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*!
\file
*/
#ifndef BLACKMISC_ITERATOR_H
#define BLACKMISC_ITERATOR_H
#include <QScopedPointer>
#include <algorithm>
#include <type_traits>
#include <iterator>
#include <utility>
namespace BlackMisc
{
namespace Iterators
{
/*!
* \brief Generic type-erased const forward iterator with value semantics.
* \tparam T the value_type of the container being iterated over.
*
* Can take any suitable iterator type as its implementation at runtime.
*/
template <class T> class ConstForwardIterator
{
public:
//! \brief STL compatibility
//! @{
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef const T *pointer;
typedef const T &reference;
typedef const T *const_pointer;
typedef const T &const_reference;
typedef std::forward_iterator_tag iterator_category;
//! @}
//! \brief Default constructor.
ConstForwardIterator() {}
/*!
* \brief Constructor.
* \param i
*/
template <class I> ConstForwardIterator(I i) : m_pimpl(new Pimpl<I>(std::move(i))) {}
/*!
* \brief Copy constructor.
* \param other
*/
ConstForwardIterator(const ConstForwardIterator &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {}
/*!
* \brief Move constructor.
* \param other
*/
ConstForwardIterator(ConstForwardIterator &&other) : m_pimpl(other.m_pimpl.take()) {}
/*!
* \brief Assignment.
* \param i
* \return
*/
template <class I> ConstForwardIterator &operator =(I i) { m_pimpl.reset(new Pimpl<I>(std::move(i))); return *this; }
/*!
* \brief Copy assignment.
* \param other
* \return
*/
ConstForwardIterator &operator =(const ConstForwardIterator &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; }
/*!
* \brief Move assignment.
* \param other
* \return
*/
ConstForwardIterator &operator =(ConstForwardIterator &&other) { m_pimpl.reset(other.m_pimpl.take()); return *this; }
/*!
* \brief Returns a reference to the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
const_reference operator *() const { Q_ASSERT(m_pimpl); return **pimpl(); }
/*!
* \brief Arrow operator provides access to members of the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
const_pointer operator ->() const { Q_ASSERT(m_pimpl); return &**pimpl(); }
/*!
* \brief Prefix increment operator advances the iterator.
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
ConstForwardIterator &operator ++() { Q_ASSERT(m_pimpl); ++*pimpl(); return *this; }
/*!
* \brief Postfix increment operator advances the iterator.
* \return Copy of the iterator in the old position.
* \pre The iterator must be initialized and valid.
*/
ConstForwardIterator operator ++(int) { Q_ASSERT(m_pimpl); auto copy = *this; ++*pimpl(); return copy; }
/*!
* \brief Advance the iterator by a certain amount.
* \param n
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
ConstForwardIterator operator +=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() += n; return *this; }
/*!
* \brief Advance the iterator by a certain amount.
* \param n
* \return Copy of the iterator in its new position.
* \pre The iterator must be initialized and valid.
*/
ConstForwardIterator operator +(difference_type n) const { auto copy = *this; return copy += n; }
/*!
* \brief Test for equality.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator ==(const ConstForwardIterator &other) const { return (pimpl() && other.pimpl()) ? *pimpl() == *other.pimpl() : pimpl() == other.pimpl(); }
/*!
* \brief Test for inequality.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator !=(const ConstForwardIterator &other) const { return !(*this == other); }
/*!
* \brief Return opaque pointer to underlying implementation iterator object.
* \return
* \pre The iterator must have been initialized.
* \todo Returning by void* is rotten, but GCC gives a very cryptic error if I make it a function template with a cast inside.
*/
void *getImpl() { return pimpl()->impl(); }
private:
class PimplBase
{
public:
virtual ~PimplBase() {}
virtual PimplBase *clone() const = 0;
virtual const_reference operator *() const = 0;
virtual void operator ++() = 0;
virtual void operator +=(difference_type) = 0;
virtual bool operator ==(const PimplBase &) const = 0;
virtual void *impl() = 0;
};
template <class I> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename std::iterator_traits<I>::value_type>::value,
"ConstForwardIterator must be initialized from an iterator with the same value_type.");
Pimpl(I &&i) : m_impl(std::move(i)) {}
virtual PimplBase *clone() const { return new Pimpl(*this); }
virtual const_reference operator *() const { return *m_impl; }
virtual void operator ++() { ++m_impl; }
virtual void operator +=(difference_type n) { m_impl += n; }
virtual bool operator ==(const PimplBase &other) const { return m_impl == static_cast<const Pimpl&>(other).m_impl; }
virtual void *impl() { return &m_impl; }
private:
I m_impl;
};
typedef QScopedPointer<PimplBase> PimplPtr;
PimplPtr m_pimpl;
// using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const
PimplBase *pimpl() { return m_pimpl.data(); }
const PimplBase *pimpl() const { return m_pimpl.data(); }
};
/*!
* \brief Generic type-erased const bidirectional iterator with value semantics.
* \tparam T the value_type of the container being iterated over.
*
* Can take any suitable iterator type as its implementation at runtime.
*/
template <class T> class ConstBidirectionalIterator
{
public:
//! \brief STL compatibility
//! @{
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef const T *pointer;
typedef const T &reference;
typedef const T *const_pointer;
typedef const T &const_reference;
typedef std::bidirectional_iterator_tag iterator_category;
//! @}
//! \brief Default constructor.
ConstBidirectionalIterator() {}
/*!
* \brief Constructor.
* \param i
*/
template <class I> ConstBidirectionalIterator(I i) : m_pimpl(new Pimpl<I>(std::move(i))) {}
/*!
* \brief Copy constructor.
* \param other
*/
ConstBidirectionalIterator(const ConstBidirectionalIterator &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {}
/*!
* \brief Move constructor.
* \param other
*/
ConstBidirectionalIterator(ConstBidirectionalIterator &&other) : m_pimpl(other.m_pimpl.take()) {}
/*!
* \brief Assignment.
* \param i
* \return
*/
template <class I> ConstBidirectionalIterator &operator =(I i) { m_pimpl.reset(new Pimpl<I>(std::move(i))); return *this; }
/*!
* \brief Copy assignment.
* \param other
* \return
*/
ConstBidirectionalIterator &operator =(const ConstBidirectionalIterator &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; }
/*!
* \brief Move assignment.
* \param other
* \return
*/
ConstBidirectionalIterator &operator =(ConstBidirectionalIterator &&other) { m_pimpl.reset(other.m_pimpl.take()); return *this; }
/*!
* \brief Returns a reference to the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
const_reference operator *() const { Q_ASSERT(m_pimpl); return **pimpl(); }
/*!
* \brief Arrow operator provides access to members of the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
const_pointer operator ->() const { Q_ASSERT(m_pimpl); return &**pimpl(); }
/*!
* \brief Prefix increment operator advances the iterator.
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator &operator ++() { Q_ASSERT(m_pimpl); ++*pimpl(); return *this; }
/*!
* \brief Postfix increment operator advances the iterator.
* \return Copy of the iterator in the old position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator operator ++(int) { Q_ASSERT(m_pimpl); auto copy = *this; ++*pimpl(); return copy; }
/*!
* \brief Prefix decrement operator backtracks the iterator.
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator &operator --() { Q_ASSERT(m_pimpl); --*pimpl(); return *this; }
/*!
* \brief Postfix decrement operator backtracks the iterator.
* \return Copy of the iterator at the old position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator operator --(int) { Q_ASSERT(m_pimpl); auto copy = *this; --*pimpl(); return copy; }
/*!
* \brief Advance the iterator by a certain amount.
* \param n
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator operator +=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() += n; return *this; }
/*!
* \brief Advance the iterator by a certain amount.
* \param n
* \return Copy of the iterator in its new position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator operator +(difference_type n) const { auto copy = *this; return copy += n; }
/*!
* \brief Backtrack the iterator by a certain amount.
* \param n
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator operator -=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() -= n; return *this; }
/*!
* \brief Backtrack the iterator by a certain amount.
* \param n
* \return Copy of the iterator in its new position.
* \pre The iterator must be initialized and valid.
*/
ConstBidirectionalIterator operator -(difference_type n) const { auto copy = *this; return copy -= n; }
/*!
* \brief Return the distance between two iterators.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
difference_type operator -(const ConstBidirectionalIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() - *other.pimpl(); }
/*!
* \brief Test for equality.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator ==(const ConstBidirectionalIterator &other) const { return (pimpl() && other.pimpl()) ? *pimpl() == *other.pimpl() : pimpl() == other.pimpl(); }
/*!
* \brief Test for inequality.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator !=(const ConstBidirectionalIterator &other) const { return !(*this == other); }
/*!
* \brief For sorting.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator <(const ConstBidirectionalIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() < *other.pimpl(); }
/*!
* \brief Return opaque pointer to underlying implementation iterator object.
* \return
* \pre The iterator must have been initialized.
* \todo Returning by void* is rotten, but GCC gives a very cryptic error if I make it a function template with a cast inside.
*/
void *getImpl() { return pimpl()->impl(); }
private:
class PimplBase
{
public:
virtual ~PimplBase() {}
virtual PimplBase *clone() const = 0;
virtual const_reference operator *() const = 0;
virtual void operator ++() = 0;
virtual void operator --() = 0;
virtual void operator +=(difference_type) = 0;
virtual void operator -=(difference_type) = 0;
virtual difference_type operator -(const PimplBase &) const = 0;
virtual bool operator ==(const PimplBase &) const = 0;
virtual bool operator <(const PimplBase &) const = 0;
virtual void *impl() = 0;
};
template <class I> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename std::iterator_traits<I>::value_type>::value,
"ConstBidirectionalIterator must be initialized from an iterator with the same value_type.");
Pimpl(I &&i) : m_impl(std::move(i)) {}
virtual PimplBase *clone() const { return new Pimpl(*this); }
virtual const_reference operator *() const { return *m_impl; }
virtual void operator ++() { ++m_impl; }
virtual void operator --() { --m_impl; }
virtual void operator +=(difference_type n) { m_impl += n; }
virtual void operator -=(difference_type n) { m_impl -= n; }
virtual difference_type operator -(const PimplBase &other) const { return m_impl - static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator ==(const PimplBase &other) const { return m_impl == static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator <(const PimplBase &other) const { return m_impl < static_cast<const Pimpl&>(other).m_impl; }
virtual void *impl() { return &m_impl; }
private:
I m_impl;
};
typedef QScopedPointer<PimplBase> PimplPtr;
PimplPtr m_pimpl;
// using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const
PimplBase *pimpl() { return m_pimpl.data(); }
const PimplBase *pimpl() const { return m_pimpl.data(); }
};
/*!
* \brief Generic type-erased non-const bidirectional iterator with value semantics.
* \tparam T the value_type of the container being iterated over.
*
* Can take any suitable iterator type as its implementation at runtime.
*/
template <class T> class BidirectionalIterator
{
public:
//! \brief STL compatibility
//! @{
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef T *pointer;
typedef T &reference;
typedef const T *const_pointer;
typedef const T &const_reference;
typedef std::bidirectional_iterator_tag iterator_category;
//! @}
//! \brief Default constructor.
BidirectionalIterator() {}
/*!
* \brief Constructor.
* \param i
*/
template <class I> BidirectionalIterator(I i) : m_pimpl(new Pimpl<I>(std::move(i))) {}
/*!
* \brief Copy constructor.
* \param other
*/
BidirectionalIterator(const BidirectionalIterator &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {}
/*!
* \brief Move constructor.
* \param other
*/
BidirectionalIterator(BidirectionalIterator &&other) : m_pimpl(other.m_pimpl.take()) {}
/*!
* \brief Assignment.
* \param i
* \return
*/
template <class I> BidirectionalIterator &operator =(I i) { m_pimpl.reset(new Pimpl<I>(std::move(i))); return *this; }
/*!
* \brief Copy assignment.
* \param other
* \return
*/
BidirectionalIterator &operator =(const BidirectionalIterator &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; }
/*!
* \brief Move assignment.
* \param other
* \return
*/
BidirectionalIterator &operator =(BidirectionalIterator &&other) { m_pimpl.reset(other.m_pimpl.take()); return *this; }
/*!
* \brief Returns a reference to the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
const_reference operator *() const { Q_ASSERT(m_pimpl); return **pimpl(); }
/*!
* \brief Returns a reference to the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
reference operator *() { Q_ASSERT(m_pimpl); return **pimpl(); }
/*!
* \brief Arrow operator provides access to members of the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
const_pointer operator ->() const { Q_ASSERT(m_pimpl); return &**pimpl(); }
/*!
* \brief Arrow operator provides access to members of the object pointed to.
* \return
* \pre The iterator must be initialized and valid.
*/
pointer operator ->() { Q_ASSERT(m_pimpl); return &**pimpl(); }
/*!
* \brief Prefix increment operator advances the iterator.
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator &operator ++() { Q_ASSERT(m_pimpl); ++*pimpl(); return *this; }
/*!
* \brief Postfix increment operator advances the iterator.
* \return Copy of the iterator in the old position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator operator ++(int) { Q_ASSERT(m_pimpl); auto copy = *this; ++*pimpl(); return copy; }
/*!
* \brief Prefix decrement operator backtracks the iterator.
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator &operator --() { Q_ASSERT(m_pimpl); --*pimpl(); return *this; }
/*!
* \brief Postfix decrement operator backtracks the iterator.
* \return Copy of the iterator at the old position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator operator --(int) { Q_ASSERT(m_pimpl); auto copy = *this; --*pimpl(); return copy; }
/*!
* \brief Advance the iterator by a certain amount.
* \param n
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator operator +=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() += n; return *this; }
/*!
* \brief Advance the iterator by a certain amount.
* \param n
* \return Copy of the iterator in its new position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator operator +(difference_type n) const { auto copy = *this; return copy += n; }
/*!
* \brief Backtrack the iterator by a certain amount.
* \param n
* \return Reference to the iterator at the new position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator operator -=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() -= n; return *this; }
/*!
* \brief Backtrack the iterator by a certain amount.
* \param n
* \return Copy of the iterator in its new position.
* \pre The iterator must be initialized and valid.
*/
BidirectionalIterator operator -(difference_type n) const { auto copy = *this; return copy -= n; }
/*!
* \brief Return the distance between two iterators.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
difference_type operator -(const BidirectionalIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() - *other.pimpl(); }
/*!
* \brief Test for equality.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator ==(const BidirectionalIterator &other) const { return (pimpl() && other.pimpl()) ? *pimpl() == *other.pimpl() : pimpl() == other.pimpl(); }
/*!
* \brief Test for inequality.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator !=(const BidirectionalIterator &other) const { return !(*this == other); }
/*!
* \brief For sorting.
* \param other
* \return
* \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend.
*/
bool operator <(const BidirectionalIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() < *other.pimpl(); }
/*!
* \brief Return opaque pointer to underlying implementation iterator object.
* \return
* \pre The iterator must have been initialized.
* \todo Returning by void* is rotten, but GCC gives a very cryptic error if I make it a function template with a cast inside.
*/
void *getImpl() { return pimpl()->impl(); }
private:
class PimplBase
{
public:
virtual ~PimplBase() {}
virtual PimplBase *clone() const = 0;
virtual const_reference operator *() const = 0;
virtual reference operator *() = 0;
virtual void operator ++() = 0;
virtual void operator --() = 0;
virtual void operator +=(difference_type) = 0;
virtual void operator -=(difference_type) = 0;
virtual difference_type operator -(const PimplBase &) const = 0;
virtual bool operator ==(const PimplBase &) const = 0;
virtual bool operator <(const PimplBase &) const = 0;
virtual void *impl() = 0;
};
template <class I> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename std::iterator_traits<I>::value_type>::value,
"BidirectionalIterator must be initialized from an iterator with the same value_type.");
Pimpl(I &&i) : m_impl(std::move(i)) {}
virtual PimplBase *clone() const { return new Pimpl(*this); }
virtual const_reference operator *() const { return *m_impl; }
virtual reference operator *() { return *m_impl; }
virtual void operator ++() { ++m_impl; }
virtual void operator --() { --m_impl; }
virtual void operator +=(difference_type n) { m_impl += n; }
virtual void operator -=(difference_type n) { m_impl -= n; }
virtual difference_type operator -(const PimplBase &other) const { return m_impl - static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator ==(const PimplBase &other) const { return m_impl == static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator <(const PimplBase &other) const { return m_impl < static_cast<const Pimpl&>(other).m_impl; }
virtual void *impl() { return &m_impl; }
private:
I m_impl;
};
typedef QScopedPointer<PimplBase> PimplPtr;
PimplPtr m_pimpl;
// using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const
PimplBase *pimpl() { return m_pimpl.data(); }
const PimplBase *pimpl() const { return m_pimpl.data(); }
};
} //namespace Iterators
} //namespace BlackMisc
#endif //BLACKMISC_ITERATOR_H

View File

@@ -6,9 +6,9 @@
#ifndef BLACKMISC_PQPHYSICALQUANTITY_H
#define BLACKMISC_PQPHYSICALQUANTITY_H
#include "blackmisc/mathematics.h"
#include "blackmisc/pqbase.h"
#include "blackmisc/pqunits.h"
#include "blackmisc/mathematics.h"
#include "blackmisc/debug.h"
#include <QtDBus/QDBusMetaType>
#include <QtGlobal>

146
src/blackmisc/predicates.h Normal file
View File

@@ -0,0 +1,146 @@
/* Copyright (C) 2013 VATSIM Community / contributors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*!
\file
*/
#ifndef BLACKMISC_PREDICATES_H
#define BLACKMISC_PREDICATES_H
#include <functional>
#include <QObject>
namespace BlackMisc
{
namespace Predicates
{
#ifdef Q_COMPILER_VARIADIC_TEMPLATES
namespace Private
{
//! \internal
template <class...> struct MemberEqual;
template <class T, class M, class V> struct MemberEqual<T, M, V>
{
M m;
V v;
MemberEqual(M m_, V v_) : m(m_), v(v_) {}
bool operator()(const T &obj) const { return (obj.*m)() == v; }
};
template <class T, class M, class V, class... Tail> struct MemberEqual<T, M, V, Tail...>
{
MemberEqual<T, M, V> head;
MemberEqual<T, Tail...> tail;
MemberEqual(M m, V v, Tail... tail_) : head(m, v), tail(tail_...) {}
bool operator()(const T &obj) const { return head(obj) && tail(obj); }
};
//! \internal
template <class...> struct MemberLess;
template <class T, class M> struct MemberLess<T, M>
{
M m;
MemberLess(M m_) : m(m_) {}
bool operator()(const T &a, const T &b) const { return (a.*m)() < (b.*m)(); }
bool isStable(const T &a, const T &b) const { return (a.*m)() != (b.*m)(); }
};
template <class T, class M, class... Tail> struct MemberLess<T, M, Tail...>
{
MemberLess<T, M> head;
MemberLess<T, Tail...> tail;
MemberLess(M m, Tail... tail_) : head(m), tail(tail_...) {}
bool operator()(const T &a, const T &b) const { return head.isStable(a, b) ? head(a, b) : tail(a, b); }
};
} //namespace Private
/*!
* \brief Predicate which tests whether some member functions return some values.
* \param vs Pairs of { pointer to member function of T, value to compare it against }.
* \return A unary functor whose operator() which will perform the actual test.
*/
template <class T, class... Ts>
typename Private::MemberEqual<T, Ts...> MemberEqual(Ts... vs)
{
return typename Private::MemberEqual<T, Ts...>(vs...);
}
/*!
* \brief Predicate which compares the return values of some member functions of two objects.
* \param vs Pointers to member functions of T.
* \return A binary functor whose operator() which will perform the actual test.
*/
template <class T, class... Ts>
typename Private::MemberLess<T, Ts...> MemberLess(Ts... vs)
{
return typename Private::MemberLess<T, Ts...>(vs...);
}
#else //!Q_COMPILER_VARIADIC_TEMPLATES
template <class T, class M1, class V1>
typename std::function<bool(const T &)> MemberEqual(M1 m1, V1 v1)
{
return [ = ](const T & obj) -> bool { return (obj.*m1)() == v1; };
}
template <class T, class M1, class V1, class M2, class V2>
typename std::function<bool(const T &)> MemberEqual(M1 m1, V1 v1, M2 m2, V2 v2)
{
return [ = ](const T & obj) -> bool
{
return (obj.*m1)() == v1 &&
(obj.*m2)() == v2;
};
}
template <class T, class M1, class V1, class M2, class V2, class M3, class V3>
typename std::function<bool(const T &)> MemberEqual(M1 m1, V1 v1, M2 m2, V2 v2, M3 m3, V3 v3)
{
return [ = ](const T & obj) -> bool
{
return (obj.*m1)() == v1 &&
(obj.*m2)() == v2 &&
(obj.*m3)() == v3;
};
}
template <class T, class M1>
typename std::function<bool(const T &, const T &)> MemberLess(M1 m1)
{
return [ = ](const T & a, const T & b) -> bool { return (a.*m1)() < (b.*m1)(); };
}
template <class T, class M1, class M2>
typename std::function<bool(const T &, const T &)> MemberLess(M1 m1, M2 m2)
{
return [ = ](const T & a, const T & b) -> bool { if ((a.*m1)() != (b.*m1)()) return (a.*m1)() < (b.*m1)();
return (a.*m2)() < (b.*m2)();
};
}
template <class T, class M1, class M2, class M3>
typename std::function<bool(const T &, const T &)> MemberLess(M1 m1, M2 m2, M3 m3)
{
return [ = ](const T & a, const T & b) -> bool { if ((a.*m1)() != (b.*m1)()) return (a.*m1)() < (b.*m1)();
if ((a.*m2)() != (b.*m2)()) return (a.*m2)() < (b.*m2)();
return (a.*m3)() < (b.*m3)();
};
}
#endif //!Q_COMPILER_VARIADIC_TEMPLATES
} //namespace Predicates
} //namespace BlackMisc
#endif //BLACKMISC_PREDICATES_H

355
src/blackmisc/sequence.h Normal file
View File

@@ -0,0 +1,355 @@
/* Copyright (C) 2013 VATSIM Community / authors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*!
\file
*/
#ifndef BLACKMISC_SEQUENCE_H
#define BLACKMISC_SEQUENCE_H
#include "iterator.h"
#include "containerbase.h"
#include <QScopedPointer>
#include <algorithm>
#include <type_traits>
#include <iterator>
#include <utility>
namespace BlackMisc
{
/*!
* \brief Generic type-erased sequential container with value semantics.
* \tparam T the type of elements contained.
*
* Can take any suitable container class as its implementation at runtime.
*/
template <class T>
class CSequence : public CContainerBase<CSequence, T>
{
public:
//! \brief STL compatibility
//! @{
typedef T key_type;
typedef T value_type;
typedef T &reference;
typedef const T &const_reference;
typedef T *pointer;
typedef const T *const_pointer;
typedef typename Iterators::ConstBidirectionalIterator<T> const_iterator;
typedef typename Iterators::BidirectionalIterator<T> iterator;
typedef ptrdiff_t difference_type;
typedef int size_type;
//! @}
/*!
* \brief Default constructor.
*/
CSequence() : m_pimpl(new Pimpl<QList<T>>(QList<T>())) {}
/*!
* \brief Constructor.
* \tparam C Becomes the sequence's implementation type.
* \param c Initial value for the sequence; typically empty, but could contain elements.
*/
template <class C> CSequence(C c) : m_pimpl(new Pimpl<C>(std::move(c))) {}
/*!
* \brief Copy constructor.
* \param other
*/
CSequence(const CSequence &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {}
/*!
* \brief Move constructor.
* \param other
*/
CSequence(CSequence &&other) : m_pimpl(other.m_pimpl.take()) {}
/*!
* \brief Assignment.
* \tparam C Becomes the sequence's new implementation type.
* \param c New value for the sequence; typically empty, but could contain elements.
*/
template <class C> CSequence &operator =(C c) { m_pimpl.reset(new Pimpl<C>(std::move(c))); return *this; }
/*!
* \brief Copy assignment.
* \param other
* \return
*/
CSequence &operator =(const CSequence &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; }
/*!
* \brief Move assignment.
* \param other
* \return
*/
CSequence &operator =(CSequence &&other) { m_pimpl.reset(other.m_pimpl.take()); return *this; }
/*!
* \brief Change the implementation type but keep all the same elements, by copying them into the new implementation.
* \tparam C Becomes the sequence's new implementation type.
*/
template <class C> void changeImpl(C = C()) { CSequence c = C(); for (auto i = cbegin(); i != cend(); ++i) c.push_back(*i); *this = std::move(c); }
/*!
* \brief Like changeImpl, but uses the implementation type of another sequence.
* \param other
* \pre The other sequence must be initialized.
*/
void useImplOf(const CSequence &other) { PimplPtr p = other.pimpl()->cloneEmpty(); for (auto i = cbegin(); i != cend(); ++i) p->push_back(*i); m_pimpl.reset(p.take()); }
/*!
* \brief Returns iterator at the beginning of the sequence.
* \return
*/
iterator begin() { return pimpl() ? pimpl()->begin() : iterator(); }
/*!
* \brief Returns iterator at the beginning of the sequence.
* \return
*/
const_iterator begin() const { return pimpl() ? pimpl()->begin() : const_iterator(); }
/*!
* \brief Returns iterator at the beginning of the sequence.
* \return
*/
const_iterator cbegin() const { return pimpl() ? pimpl()->cbegin() : const_iterator(); }
/*!
* \brief Returns iterator one past the end of the sequence.
* \return
*/
iterator end() { return pimpl() ? pimpl()->end() : iterator(); }
/*!
* \brief Returns iterator one past the end of the sequence.
* \return
*/
const_iterator end() const { return pimpl() ? pimpl()->end() : const_iterator(); }
/*!
* \brief Returns iterator one past the end of the sequence.
* \return
*/
const_iterator cend() const { return pimpl() ? pimpl()->cend() : const_iterator(); }
/*!
* \brief Swap this sequence with another.
* \param other
*/
void swap(CSequence &other) { m_pimpl.swap(other.m_pimpl); }
/*!
* \brief Access an element by its index.
* \param index
* \return
* \pre The sequence must be initialized and the index in bounds.
*/
reference operator [](size_type index) { Q_ASSERT(pimpl()); return pimpl()->operator [](index); }
/*!
* \brief Access an element by its index.
* \param index
* \return
* \pre The sequence must be initialized and the index in bounds.
*/
const_reference operator [](size_type index) const { Q_ASSERT(pimpl()); return pimpl()->operator [](index); }
/*!
* \brief Access the first element.
* \return
* \pre The sequence must not be empty.
*/
reference front() { Q_ASSERT(!empty()); return pimpl()->front(); }
/*!
* \brief Access the first element.
* \return
* \pre The sequence must not be empty.
*/
const_reference front() const { Q_ASSERT(!empty()); return pimpl()->front(); }
/*!
* \brief Access the last element.
* \return
* \pre The sequence must not be empty.
*/
reference back() { Q_ASSERT(!empty()); return pimpl()->back(); }
/*!
* \brief Access the last element.
* \return
* \pre The sequence must not be empty.
*/
const_reference back() const { Q_ASSERT(!empty()); return pimpl()->back(); }
/*!
* \brief Returns number of elements in the sequence.
* \return
*/
size_type size() const { return pimpl() ? pimpl()->size() : 0; }
/*!
* \brief Returns true if the sequence is empty.
* \return
*/
bool empty() const { return pimpl() ? pimpl()->empty() : true; }
/*!
* \brief Synonym for empty.
* \return
*/
bool isEmpty() const { return empty(); }
/*!
* \brief Removes all elements in the sequence.
*/
void clear() { if (pimpl()) pimpl()->clear(); }
/*!
* \brief Inserts an element into the sequence.
* \param before
* \param value
* \return An iterator to the position where value was inserted.
* \pre The sequence must be initialized.
*/
iterator insert(iterator before, const T &value) { Q_ASSERT(pimpl()); return pimpl()->insert(before, value); }
/*!
* \brief Appends an element at the end of the sequence.
* \param value
* \pre The sequence must be initialized.
*/
void push_back(const T &value) { Q_ASSERT(pimpl()); pimpl()->push_back(value); }
/*!
* \brief Synonym for push_back.
* \param value
* \pre The sequence must be initialized.
*/
void insert(const T &value) { push_back(value); }
/*!
* \brief Removes an element at the end of the sequence.
* \pre The sequence must contain at least one element.
*/
void pop_back() { Q_ASSERT(!empty()); pimpl()->pop_back(); }
/*!
* \brief Remove the element pointed to by the given iterator.
* \param pos
* \return An iterator to the position of the next element after the one removed.
* \pre The sequence must be initialized.
*/
iterator erase(iterator pos) { Q_ASSERT(pimpl()); return pimpl()->erase(pos); }
/*!
* \brief Remove the range of elements between two iterators.
* \param it1
* \param it2
* \return An iterator to the position of the next element after the one removed.
* \pre The sequence must be initialized.
*/
iterator erase(iterator it1, iterator it2) { Q_ASSERT(pimpl()); return pimpl()->erase(it1, it2); }
/*!
* \brief Test for equality.
* \param other
* \return
* \todo Improve inefficient implementation.
*/
bool operator ==(const CSequence &other) const { return (empty() && other.empty()) ? true : (size() != other.size() ? false : *pimpl() == *other.pimpl()); }
/*!
* \brief Test for inequality.
* \param other
* \return
* \todo Improve inefficient implementation.
*/
bool operator !=(const CSequence &other) const { return !(*this == other); }
private:
class PimplBase
{
public:
virtual ~PimplBase() {}
virtual PimplBase *clone() const = 0;
virtual PimplBase *cloneEmpty() const = 0;
virtual iterator begin() = 0;
virtual const_iterator begin() const = 0;
virtual const_iterator cbegin() const = 0;
virtual iterator end() = 0;
virtual const_iterator end() const = 0;
virtual const_iterator cend() const = 0;
virtual reference operator [](size_type index) = 0;
virtual const_reference operator [](size_type index) const = 0;
virtual reference front() = 0;
virtual const_reference front() const = 0;
virtual reference back() = 0;
virtual const_reference back() const = 0;
virtual size_type size() const = 0;
virtual bool empty() const = 0;
virtual void clear() = 0;
virtual iterator insert(iterator pos, const T &value) = 0;
virtual void push_back(const T &value) = 0;
virtual void pop_back() = 0;
virtual iterator erase(iterator pos) = 0;
virtual iterator erase(iterator it1, iterator it2) = 0;
virtual bool operator ==(const PimplBase &other) const = 0;
};
template <class C> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename C::value_type>::value, "CSequence must be initialized from a container with the same value_type.");
Pimpl(C &&c) : m_impl(std::move(c)) {}
PimplBase *clone() const { return new Pimpl(*this); }
PimplBase *cloneEmpty() const { return new Pimpl(C()); }
iterator begin() { return m_impl.begin(); }
const_iterator begin() const { return m_impl.cbegin(); }
const_iterator cbegin() const { return m_impl.cbegin(); }
iterator end() { return m_impl.end(); }
const_iterator end() const { return m_impl.cend(); }
const_iterator cend() const { return m_impl.cend(); }
reference operator [](size_type index) { return m_impl[index]; }
const_reference operator [](size_type index) const { return m_impl[index]; }
reference front() { return m_impl.front(); }
const_reference front() const { return m_impl.front(); }
reference back() { return m_impl.back(); }
const_reference back() const { return m_impl.back(); }
size_type size() const { return m_impl.size(); }
bool empty() const { return m_impl.empty(); }
void clear() { m_impl.clear(); }
iterator insert(iterator pos, const T &value) { return m_impl.insert(*static_cast<const typename C::iterator*>(pos.getImpl()), value); }
void push_back(const T &value) { m_impl.push_back(value); }
void pop_back() { m_impl.pop_back(); }
iterator erase(iterator pos) { return m_impl.erase(*static_cast<const typename C::iterator*>(pos.getImpl())); }
iterator erase(iterator it1, iterator it2) { return m_impl.erase(*static_cast<const typename C::iterator*>(it1.getImpl(), it2.getImpl())); }
bool operator ==(const PimplBase &other) const { Pimpl copy = C(); for (auto i = other.cbegin(); i != other.cend(); ++i) copy.push_back(*i); return m_impl == copy.m_impl; }
private:
C m_impl;
};
typedef QScopedPointer<PimplBase> PimplPtr;
PimplPtr m_pimpl;
// using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const
PimplBase *pimpl() { return m_pimpl.data(); }
const PimplBase *pimpl() const { return m_pimpl.data(); }
};
} //namespace BlackMisc
Q_DECLARE_METATYPE(BlackMisc::CSequence<int>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<uint>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<qlonglong>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<qulonglong>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<double>)
#endif //BLACKMISC_SEQUENCE_H

View File

@@ -180,3 +180,8 @@ namespace BlackMisc
return argument;
}
}
uint qHash(const BlackMisc::CValueObject &value)
{
return value.getValueHash();
}

View File

@@ -1,6 +1,19 @@
#ifndef BLACKMISC_VALUEOBJECT_H
#define BLACKMISC_VALUEOBJECT_H
namespace BlackMisc
{
class CValueObject;
}
/*!
* qHash overload, needed for storing CValueObject in a QSet.
* \param value
* \return
*/
// Appears before all #include directives, to workaround an issue with GCC where the overload is not visible in QSet
unsigned int qHash(const BlackMisc::CValueObject &value);
#include "blackmisc/debug.h"
#include <QtDBus/QDBusMetaType>
#include <QString>

View File

@@ -3,6 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "testcontainers.h"
#include "testblackmiscmain.h"
namespace BlackMiscTest
@@ -18,10 +19,12 @@ namespace BlackMiscTest
CTestAviation avBaseTests;
CTestVectorMatrix vmTests;
CTestGeo geoTests;
CTestContainers containerTests;
status |= QTest::qExec(&pqBaseTests, argc, argv);
status |= QTest::qExec(&avBaseTests, argc, argv);
status |= QTest::qExec(&vmTests, argc, argv);
status |= QTest::qExec(&geoTests, argc, argv);
status |= QTest::qExec(&containerTests, argc, argv);
}
return status;

View File

@@ -0,0 +1,68 @@
/* Copyright (C) 2013 VATSIM Community / contributors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "blackmisc/collection.h"
#include "blackmisc/sequence.h"
#include "testcontainers.h"
#include <QList>
#include <QString>
#include <vector>
using namespace BlackMisc;
namespace BlackMiscTest
{
void CTestContainers::collectionBasics()
{
CCollection<int> c1;
QVERIFY2(c1.empty(), "Uninitialized collection is empty");
CCollection<int> c2 = QList<int>();
QVERIFY2(c1 == c2, "Uninitialized and empty collections are equal");
c1.changeImpl(std::vector<int>());
QVERIFY2(c1 == c2, "Two empty collections are equal");
c1.insert(1);
QVERIFY2(c1 != c2, "Different collections are not equal");
QVERIFY2(c1.size() == 1, "Collection has expected size");
c2.insert(1);
QVERIFY2(c1 == c2, "Collections with equal elements are equal");
c1.changeImpl(QSet<int>());
QVERIFY2(c1 == c2, "Collection stays equal after changing implementation");
c1.clear();
QVERIFY2(c1.empty(), "Cleared collection is empty");
c1.insert(2);
QVERIFY2(c1 != c2, "Collections with different elements are not equal");
c1 = c2;
QVERIFY2(c1 == c2, "Copied collection is equal");
}
void CTestContainers::sequenceBasics()
{
CSequence<int> s1;
QVERIFY2(s1.empty(), "Uninitialized sequence is empty");
CSequence<int> s2 = QList<int>();
QVERIFY2(s1 == s2, "Uninitialized and empty sequence are equal");
s1.changeImpl(std::vector<int>());
QVERIFY2(s1 == s2, "Two empty sequences are equal");
s1.push_back(1);
QVERIFY2(s1 != s2, "Different sequences are not equal");
QVERIFY2(s1.size() == 1, "Sequence has expected size");
s2.push_back(1);
QVERIFY2(s1 == s2, "Sequences with equal elements are equal");
s1.changeImpl(QVector<int>());
QVERIFY2(s1 == s2, "Sequence stays equal after changing implementation");
s1.clear();
QVERIFY2(s1.empty(), "Cleared sequence is empty");
s1.push_back(2);
QVERIFY2(s1 != s2, "Sequences with different elements are not equal");
s1 = s2;
QVERIFY2(s1 == s2, "Copied sequence is equal");
QVERIFY2(s1[0] = 1, "Subscripted element mutation");
QVERIFY2(s1[0] == 1, "Subscripted element has expected value");
QVERIFY2(s1.back() == 1, "Last element has expected value");
}
} //namespace BlackMiscTest

View File

@@ -0,0 +1,28 @@
/* Copyright (C) 2013 VATSIM Community / contributors
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BLACKMISCTEST_TESTCONTAINERS_H
#define BLACKMISCTEST_TESTCONTAINERS_H
#include <QtTest/QtTest>
namespace BlackMiscTest
{
class CTestContainers : public QObject
{
Q_OBJECT
public:
explicit CTestContainers(QObject *parent = 0) : QObject(parent) {}
private slots:
void collectionBasics();
void sequenceBasics();
};
} //namespace BlackMiscTest
#endif //BLACKMISCTEST_TESTCONTAINERS_H