Neurotransmitter
Neurotransmitter

Reputation: 6817

Reuse existing QDirIterator in Qt 5

I use multiple QDirIterators in a single scope of a Qt 5 project. They typically look like this:

QDirIterator i(QDir::currentPath(), QDir::Dirs | QDir::NoDotAndDotDot);
while (i.hasNext()) {
    doSomething();
};

Now I use multiple objects all with theirs own names (i0, i1, i2 etc.), and I wonder how can I use just one name across the project, i in this example? How should I decommission existing QDirIterator to reuse it?

Upvotes: 1

Views: 516

Answers (2)

You can implement a forward sequential iterator adapter to QDirIterator:

// https://github.com/KubaO/stackoverflown/tree/master/questions/dir-iterator-39133673
#include <QtCore>
#include <boost/optional.hpp>
#include <boost/iterator/iterator_facade.hpp>

class QDir_iterator : public
        boost::iterator_facade<QDir_iterator, QString,
        boost::forward_traversal_tag, QString>
{
    friend class boost::iterator_core_access;
    boost::optional<QDirIterator &> it;
    bool equal(const QDir_iterator & other) const {
        return **this == *other;
    }
    QString dereference() const {
        return it != boost::none ? it->filePath() : QString{};
    }
    void increment() {
        it->next();
    }
public:
    QDir_iterator() = default;
    QDir_iterator(QDirIterator & dir) : it{dir} {
        it->next();
    }
};

QDir_iterator begin(QDirIterator & dir) { return QDir_iterator{dir}; }
QDir_iterator end(QDirIterator &) { return QDir_iterator{}; }

The adapter is usable on a QDirIterator, thus you can pass a QDirIterator directly to a range-for:

void test1() {
    auto curr = QDir::current();
    auto entries = curr.entryList();
    int i = 0;
    for (auto dir : QDirIterator{curr}) ++i;
    Q_ASSERT(i == entries.count());
}

You can pass any necessary flags or filters to QDirIterator, of course. The adapter will follow them.

If you're allergic to Boost, you could factor out iterating a directory to "save" having to deal with a braced scope:

template <typename F>
void iterate(QDirIterator && it, F && fun) {
     while (it.hasNext())
        fun(it.next());
}

Iteration uses a temporary iterator - uniform initialization makes it quite painless:

void test2() {
    auto curr = QDir::current();
    auto entries = curr.entryList();
    int i = 0, j = 0;
    iterate({curr}, [&](const QString &){ ++i; });
    Q_ASSERT(entries.length() == i);
    iterate({curr}, [&](const QString &){ ++j; });
    Q_ASSERT(entries.length() == j);
}

Upvotes: 0

Pavel Strakhov
Pavel Strakhov

Reputation: 40512

C++ allows you to introduce a new scope anywhere you need, for example:

{
  QDirIterator i(path1, QDir::Dirs | QDir::NoDotAndDotDot);
  while (i.hasNext()) {
    doSomething();
  };
}
{
  QDirIterator i(path2, QDir::Dirs | QDir::NoDotAndDotDot);
  while (i.hasNext()) {
    doSomething();
  };
}

Upvotes: 2

Related Questions