1

I am trying to use std::filesystem::recursive_directory_iterator to iterate through my C drive and print file paths but I keep running into the following exception: terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error' what(): filesystem error: cannot increment recursive directory iterator: Invalid argument

I have tried:

#include <iostream>
#include <filesystem>

int main() {
    for (auto const& dir_entry : std::filesystem::recursive_directory_iterator("C:\\")) {
        try {
            std::cout << dir_entry << std::endl;
        } catch (std::filesystem::__cxx11::filesystem_error) {
            continue;
        }
    }
    
    return 0;
}

I was expecting it to continue to the next iteration after running into the exception but I simply received the same error message.

I also tried:

#include <iostream>
#include <filesystem>

int main() {
    try {
        for (auto const& dir_entry : std::filesystem::recursive_directory_iterator("C:\\")) {
            std::cout << dir_entry << std::endl;
        }
    } catch (std::filesystem::__cxx11::filesystem_error) {
        std::cout << "Ran into exception";
    }
    
    return 0;
}

which caught the problem and the catch block was executed but I cannot continue to the next iteration because the try/catch statements are outside of the for loop.

Calabran
  • 11
  • 2

2 Answers2

1

You can't continue to the next iteration because THERE IS NO NEXT ITERATION.

cannot increment recursive directory iterator

In case of other exceptions your first code snippet may work, but this one will not allow iteration to continue.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

Here is a workaround.

#include <iostream>
#include <filesystem>


int main(int argc, char** argv)
{
    if(argc < 1)
        return 1;
    for(std::filesystem::recursive_directory_iterator i {argv[1]}, saved, end;
          i != end; ) {
        std::cout << *i << '\n';
        saved = i;
        try {
            ++i;
        } catch(std::filesystem::filesystem_error& err) {
            std::cout << "Error: " << err.what() << '\n';
            i = std::move(saved);
            i.disable_recursion_pending();
            ++i;
        }
    }
}

Basically, you can save the iterator in a copy and then disable recursion. From what I can tell, at least for GCC, copying the iterator just copies a shared_ptr and a bool. This means it's not a true copy but since the shared state is not modified if an exception is thrown, this works. Basically, we just need to make sure that the shared state is not discarded when the iterator resets itself to the end state after the exception.

Of course the better option is to set the skip_permission_denied flag. But sometimes you may want to know if a directory without permissions exist. Then I think this is the best option. Alternatively you could probe each directory first without using the iterator to see if you can read it, then call disable_recursion_pending if required.

Homer512
  • 9,144
  • 2
  • 8
  • 25