refs #466 Resolved TODO items in containers.

This commit is contained in:
Mathew Sutcliffe
2015-09-14 21:35:27 +01:00
parent 2ca457903c
commit 64a25a7860
6 changed files with 52 additions and 41 deletions

View File

@@ -17,6 +17,7 @@
#include <QScopedPointer>
#include <algorithm>
#include <type_traits>
#include <typeindex>
#include <iterator>
#include <utility>
#include <initializer_list>
@@ -356,13 +357,11 @@ namespace BlackMisc
/*!
* \brief Test for equality.
* \todo Improve inefficient implementation.
*/
bool operator ==(const CCollection &other) const { return (empty() && other.empty()) ? true : (size() != other.size() ? false : *pimpl() == *other.pimpl()); }
bool operator ==(const CCollection &other) const { return *pimpl() == *other.pimpl(); }
/*!
* \brief Test for inequality.
* \todo Improve inefficient implementation.
*/
bool operator !=(const CCollection &other) const { return !(*this == other); }
@@ -397,6 +396,8 @@ namespace BlackMisc
virtual const_iterator find(const T &value) const = 0;
virtual bool operator ==(const PimplBase &other) const = 0;
virtual void *impl() = 0;
virtual const void *impl() const = 0;
virtual std::type_index implType() const = 0;
};
template <class C> class Pimpl : public PimplBase
@@ -422,10 +423,14 @@ namespace BlackMisc
iterator erase(iterator it1, iterator it2) override { while (it1 != it2) { it1 = iterator::fromImpl(m_impl.erase(*static_cast<const typename C::iterator *>(it1.getImpl()))); } return it1; }
iterator find(const T &value) override { return iterator::fromImpl(m_impl.find(value)); }
const_iterator find(const T &value) const override { return const_iterator::fromImpl(m_impl.find(value)); }
bool operator ==(const PimplBase &other) const override { Pimpl copy = C(); for (auto i = other.cbegin(); i != other.cend(); ++i) copy.insert(*i); return m_impl == copy.m_impl; }
bool operator ==(const PimplBase &other) const override { return implType() == other.implType() ? m_impl == *static_cast<const C *>(other.impl()) : size() == other.size() && std::equal(begin(), end(), other.begin()); }
void *impl() override { return &m_impl; }
const void *impl() const override { return &m_impl; }
std::type_index implType() const override { return typeid(C); }
private:
C m_impl;
bool implEquals(const PimplBase &other) const { return m_impl == *static_cast<const C *>(other.impl()); }
bool equals(const PimplBase &other) const { return size() == other.size() && std::equal(begin(), end(), other.begin()); }
// insertHelper: QOrderedSet::insert returns an iterator, but std::set::insert returns a std::pair<interator, bool>
template <class I> static I insertHelper(I i) { return i; }
template <class I> static I insertHelper(std::pair<I, bool> p) { return p.first; }

View File

@@ -411,18 +411,16 @@ namespace BlackMisc
friend bool operator !=(const CDictionary &a, const CDictionary &b) { return !(a == b); }
//! \copydoc BlackMisc::CValueObject::convertToQString
//! \todo Fix brackets
QString convertToQString(bool i18n = false) const
{
QString str = "{";
QString str;
for (auto it = m_impl.cbegin(); it != m_impl.end(); ++it)
{
str += "{";
str += CContainerHelper::stringify(it.key(), i18n) + "," + CContainerHelper::stringify(it.value(), i18n);
str += "}";
}
if (str.isEmpty()) { str = "{"; }
return str += "}";
return "{" + str + "}";
}
public:

View File

@@ -187,6 +187,9 @@ namespace BlackMisc
bool isEmpty() const { return empty(); }
//! @}
//! Returns the number of elements in the range.
difference_type size() const { return std::distance(begin(), end()); }
//! Returns the element at the beginning of the range. Undefined if the range is empty.
const_reference front() const { Q_ASSERT(!empty()); return *begin(); }

View File

@@ -80,24 +80,29 @@ namespace BlackMisc
}
template <class OBJ, class CONTAINER>
QList<CONTAINER> ITimestampObjectList<OBJ, CONTAINER>::splitByTime(qint64 msSinceEpoch, bool alreadySortedLatestFirst) const
QList<CONTAINER> ITimestampObjectList<OBJ, CONTAINER>::splitByTime(qint64 msSinceEpoch, bool sortedLatestFirst) const
{
// fixme: Split by time is one of the most frequently called functions in interpolator, so any performance improvement here counts
CONTAINER newer(this->container());
if (!alreadySortedLatestFirst) { newer.sortLatestFirst(); }
CONTAINER older;
for (auto it = newer.begin(); it != newer.end(); ++it)
QList<CONTAINER> result { {}, {} };
const auto &c = this->container();
if (sortedLatestFirst)
{
if (it->isOlderThan(msSinceEpoch))
// O(log n) comparisons and O(n) copies
struct Comparator
{
older.insert(CRange<typename CONTAINER::iterator>(it, newer.end()));
newer.erase(it, newer.end());
break;
}
bool operator()(const OBJ &a, qint64 b) const { return a.isNewerThan(b); }
bool operator()(qint64 a, const OBJ &b) const { return b.isOlderThan(a); }
};
auto it = std::upper_bound(c.begin(), c.end(), msSinceEpoch, Comparator());
std::copy(c.begin(), it, std::back_inserter(result[0]));
std::copy(it, c.end(), std::back_inserter(result[1]));
}
// before / after
return QList<CONTAINER>({newer, older});
else
{
// O(n) comparisons and O(n) copies
std::partition_copy(c.begin(), c.end(), std::back_inserter(result[0]), std::back_inserter(result[1]),
[msSinceEpoch](const OBJ & obj) { return ! obj.isNewerThan(msSinceEpoch); });
}
return result;
}
template <class OBJ, class CONTAINER>

View File

@@ -44,9 +44,9 @@ namespace BlackMisc
//! List of objects after msSinceEpoch
CONTAINER findAfter(qint64 msSinceEpoch) const;
//! Split into 2 containers, [0] >= msSinceEpoch ("newer") [b] < msSinceEpoch ("older")
//! \note Sort order: latest elements first
QList<CONTAINER> splitByTime(qint64 msSinceEpoch, bool alreadySortedLatestFirst = false) const;
//! Partition into two containers, first [0,msSinceEpoch] and second (msSinceEpoch,LLONG_MAX].
//! Within each of the two parts, the original relative ordering of the elements is preserved.
QList<CONTAINER> splitByTime(qint64 msSinceEpoch, bool sortedLatestFirst = false) const;
//! Latest value
OBJ latestValue() const;

View File

@@ -235,23 +235,23 @@ namespace BlackMiscTest
QVERIFY2(ms == ts, "Latest value not first");
// split in half
situations.sortOldestFirst(); // check that we really get latest first
QList<CAircraftSituationList> split = situations.splitByTime(ts - ((no / 2) * 10) + 1);
CAircraftSituationList before = split[0];
CAircraftSituationList after = split[1];
int beforeSize = before.size();
int afterSize = after.size();
QVERIFY(beforeSize == no / 2);
QVERIFY(afterSize == no / 2);
// check sort order, latest should
for (int i = 0; i < no; ++i)
for (bool sortLatestFirst : { false, true })
{
CAircraftSituation s = (i < no / 2) ? before[i] : after[i - no / 2];
ms = s.getMSecsSinceEpoch();
QVERIFY2(ms == ts - 10 * i, "time does not match");
sortLatestFirst ? situations.sortLatestFirst() : situations.sortOldestFirst();
qint64 splitTime = ts - ((no / 2) * 10) + 1;
QList<CAircraftSituationList> split = situations.splitByTime(splitTime);
CAircraftSituationList before = split[0];
CAircraftSituationList after = split[1];
int beforeSize = before.size();
int afterSize = after.size();
QVERIFY(beforeSize == no / 2);
QVERIFY(afterSize == no / 2);
// check partitioning
auto isNewer = [splitTime](const CAircraftSituation &as) { return as.isNewerThan(splitTime); };
QVERIFY2(std::none_of(before.cbegin(), before.cend(), isNewer), "before contains a time which is after");
QVERIFY2(std::all_of(after.cbegin(), after.cend(), isNewer), "after contains a time which is before");
}
// test shifting