I want to use methods std::filesystem::directory_iterator
or std::filesystem::recursive_directory_iterator
depending on a boolean. The code would be something like:
some_type dirIterator = isRecursive ? std::filesystem::recursive_directory_iterator : std::filesystem::directory_iterator;
for (const std::filesystem::directory_entry& entry : dirIterator(path))
{
// common code for the two iterators
}
How can I accomplish this? If there are different approaches I would like to know all to choose the one I like more, but I think I prefer the simpler one :-)
I tried to use a function pointer but I do not know the "common" return type of the two iterators:
??? (*dirIterator) (const std::filesystem::path & dirPath);
recursive_directory_iterator
and directory_iterator
are distinct types, and they are not covariant. If you try to select one of two with the conditional ? A : B
operator, there is a problem, since A
and B
are unrelated types.
However, both types of iterators share a common interface. Most importantly, they are iterable. This suggests that you could use templates to solve the problem.
namespace fs = std::filesystem; // applies to all code in this answer
template <typename DirectoryIterator>
void doStuff(DirectoryIterator iterator) {
// TODO: consider constraining this function with std::enable_if
for (const fs::directory_entry& entry : iterator) {
// ...
}
}
// alternatively, make doStuff a generic lambda:
auto doStuff = [](auto iterator) {
for (const fs::directory_entry& entry : iterator) {
// ...
}
}
// ...
if (isRecursive) doStuff(fs::recursive_directory_iterator(path));
else doStuff(fs::directory_iterator(path));
Another solution is to put whatever you were going to do in the loop into a lambda:
auto visitEntry = [](const fs::directory_entry& entry) {
// ...
};
if (isRecursive) {
for (const fs::directory_entry& entry : fs::recursive_directory_iterator(path)) {
visitEntry(entry);
}
}
else {
for (const fs::directory_entry& entry : fs::directory_iterator(path)) {
visitEntry(entry);
}
}
This produces mild code duplication, but it's conceptually simpler than the template.
A clever solution by commenter @Turtlefight is to use recursive_diretory_iterator
in both cases, and disable recursion after each iteration with disable_recursion_pending
:
auto iterator = fs::recursive_directory_iterator(path);
for (const fs::directory_entry& entry : iterator) {
// ...
if (!isRecursive) iterator.disable_recursion_pending();
}