简体   繁体   中英

Continue in range based for loop after exception

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.

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 exceptions 1 , the de-referencing operator ( *it ) and the increment operator ( ++it ).

If any of these reports an error (ie, 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, eg

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM