0

I wanted to use the std::filesystem::recursive_directory_iterator class to create a class method iterating through all subdirectories and processing found xml files.

The only way I have found on the internet to do this was using a for loop like this:

for (fs::directory_entry p : fs::recursive_directory_iterator("my_file"))
    do_something(p);

The problem is that i need to store my iterator (or atleast where it's pointing) inbetween function calls as i can only process one file at a time. I tried implementing it like this:

class C {
private:
    std::filesystem::recursive_directory_iterator it;
    std::filesystem::directory_entry p;
public:
    C(std::filesystem::path);
    std::string find_file();
};

C::C(std::filesystem::path path)
{
    it = fs::recursive_directory_iterator(path);
    p = fs::directory_entry(it.begin());
}

std::string C::find_file()
{
    do { //using do while so my function won't load the same file twice
        ++p;
    } while (!is_xml(p.path()) && p != it.end());

}

But it seems that std::filesystem::recursive_directory_iterator doesn't have begin() and end() methods and can't be compared.

I have no idea how my code is different from the working for range loop except for storing the iterator and having an extra condition.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
skrzypin
  • 3
  • 4

2 Answers2

1

If you look a std::filesystem::recursive_directory_iterator Non-member functions you can see that there is:

// range-based for loop support
begin(std::filesystem::recursive_directory_iterator)
end(std::filesystem::recursive_directory_iterator)

And then std::filesystem::begin(recursive_directory_iterator), std::filesystem::end(recursive_directory_iterator) with more details:

end(recursive_directory_iterator) Returns a default-constructed recursive_directory_iterator, which serves as the end iterator. The argument is ignored.

So you will check if it is not equal to std::end(it), so see if there are any more elements. And you have to increment it and not p.

You also need to check if it != std::end(it) before you do !is_xml(*it.path())

std::string C::find_file()
{
    do { //using do while so my function won't load the same file twice

        ++it;
    } while (it != std::end(it) && !is_xml(*it.path()));

}
t.niese
  • 39,256
  • 9
  • 74
  • 101
  • thank you, but may I also ask how does the range for loop differentiate between begin(it) for recursive_directory_iterator and it.begin() for for example vectors? – skrzypin Jul 29 '21 at 09:54
  • @skrzype if `std::begin` and `std::end` are not overloaded they will default to calling `.begin()` and `.end()` on the passed value. So instead of doing `var.begin()` and `var.end()` you could use `std::begin(var)` and `std::end(var)` for generic code. But if you look at [Range-based for loop: Explanation](https://en.cppreference.com/w/cpp/language/range-for) you can see how the range based for decides what to use. So it actually checks if the member function exists and if not it will fallback to using `std::begin`/`std::end` – t.niese Jul 29 '21 at 09:57
0

recursive_directory_iterator is already an iterator by itself (it says so right in its name), so you don't need to use begin() and end() at all. It implements operator==, operator!=, operator->, and operator++, which are all you need in this case.

Also, there is no reason for p to be a class member at all. It should be a local variable of find_file() instead (actually, in this case, it can be eliminated completely). And the loop should be a while loop instead of a do..while loop, in case the iterator is already at its "end" when find_file() is entered.

Try this instead:

class C {
private:
    std::filesystem::recursive_directory_iterator it;
public:
    C(std::filesystem::path);
    std::string find_file();
};

C::C(std::filesystem::path path)
    : it(path)
{
}

std::string C::find_file()
{
    static std::filesystem::directory_iterator end;
    while (it != end) {
        auto p = it->path();
        if (is_xml(p))
            return p.string();
        ++it;
    }
    return "";
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770