1

I have the following program that iterates through a directory and can catch exceptions:

#include <iostream>
#include <filesystem>
#include <exception>

int main() {

    const std::filesystem::path md("/");

    try {

        for(auto const& dir : std::filesystem::recursive_directory_iterator(md)) {
            std::cout << dir.path() << std::endl;
        }

    }
    catch(const std::exception& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }

    return 0;
}

The problem is, whenever an exception is thrown it exits the for loop and end the program. Is there any way to go back to the position where the for loop left off after handling the exception?

EDIT: The error is caused by the for loop recursive_directory_iterator not from inside the loop. It is guaranteed to throw the same error at the same position in the loop every time so I need continue to the loop at the position after the error is thrown.

17
  • I don't think, there's a recommendable way. Commented Feb 11, 2022 at 20:11
  • 2
    Yes. Add a try/catch block inside the loop. The catch can decide whether to continue or re-throw. Commented Feb 11, 2022 at 20:12
  • @Drew Dormann The error is being thrown by the for loop itself when it calls recursive_directory_iterator. How do I put a try catch block there? Commented Feb 11, 2022 at 20:19
  • 2
    I suggest rewriting your code to recursively use the std::filesystem::directory_iterator . This will give you more control over which directory is causing the problem. Commented Feb 11, 2022 at 20:33
  • 1
    @GaryFisher What is the actual error that is being thrown? If it is just a permission error, you can use the skip_permission_denied flag when constructing the iterator. Commented Feb 11, 2022 at 21:39

1 Answer 1

2

What you want is not fully possible with std::filesystem::recursive_directory_iterator, even if you write the loop manually.

There are two places where the iterator can throw exceptions1, the de-referencing operator (*it) and the increment operator (++it).

If any of these reports an error (i.e., throws an exception in your case), the iterator cannot be used anymore.2

For the de-referencing iterator, you can backup the iterator and skip invalid entries

auto it = std::filesystem::recursive_directory_iterator{md};
while (it != std::filesystem::recursive_directory_iterator{}) {
    const auto tmp_it = it;
    try {
        const auto& path = *tmp_it;
        // use path...
    }
    catch (std::filesystem::filesystem_error const&) {

    }

    // you can still increment it here
    ++it;
}

Unfortunately, if the increment operator reports an error (throws or reports an std::error_code), there is not much you can do to simply "skip" that element — you can handle some cases, but not everything, e.g.

std::error_code ec;
const auto backup = it;

// try increment
it.increment(ec);

// failed
if (ec) {
    it = backup;

    // the iterator tried to enter a directory and failed, disable the 
    // recursion for the next entry
    if (it.recursion_pending()) {
        it.disable_recursion_pending();

        // try again
        it.increment(ec);
    }
}

// still failed, or was not recursion_pending()
if (ec) {
    it = backup;

    // try to skip the remaining entry at the given depth go back to 
    // the parent      
    it.pop(ec);
}

// nothing much you can do here unfortunately
if (ec) {

}

1 The constructor can also report error but in this case you do not even have a valid iterator to begin with so that is not relevant for your question.

2 Standard quotes:

[filesystems#fs.class.directory.iterator.general-3]

If an iterator of type directory_­iterator reports an error or is advanced past the last directory element, that iterator shall become equal to the end iterator value. [..]

[filesystems#fs.class.rec.dir.itr.general-3]

The behavior of a recursive_­directory_­iterator is the same as a directory_­iterator unless otherwise specified.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.