Использование адаптеров расширенного диапазона с диапазоном итераторов каталогов

Изменить: я добавил некоторые решения ниже моего вопроса, основываясь на ответе Джонатана

Я хочу иметь список обычных файлов с определенным шаблоном имени в данном каталоге. Я взял один из примеров из Boost.Filesystem (повысить 1,53) и изменил его. Вот рабочая версия того, что я в основном хочу:

#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost::filesystem;

int main(int argc, char* argv[])
{
path p ("."); // search directory

try
{
if (exists(p))
{
if (is_directory(p))
{
cout << "logfiles in " << absolute(p) << '\n';
// I want to replace this part:
std::list<directory_entry> datFiles;
for(directory_iterator it(p); it != directory_iterator(); ++it)
{
if (is_regular_file(*it) && (extension(*it) == ".dat"))
{
datFiles.push_back(*it);
}
}
// part to be replaced ends here
BOOST_FOREACH(const directory_entry& de, datFiles)
{
cout << de << '\n';
}
}
}
}

catch (const filesystem_error& ex)
{
cout << ex.what() << '\n';
}

return 0;
}

Это добавляет запись всех файлов, заканчивающихся на .dat к списку записей каталога. Мне нужно больше сопоставления с образцом позже. Затем я попытался сделать то же самое с диапазонами, но потерпел неудачу. Теперь я сократил задачу до простого копирования всех записей каталога, которые также не компилируются:

    // replacement starts here
std::list<directory_entry> datFiles;
boost::iterator_range<directory_iterator> rng(directory_iterator(p), directory_iterator());should be
boost::copy(rng, datFiles.begin());
// replacement ends here

Я написал это так, потому что (tut3.cpp из boost.filesystem)

directory_iterator :: value_type is directory_entry

Так что эта копия может перебирать диапазон, получать записи и сохранять их в моем списке. Тем не менее, я получаю много сообщений об ошибках от gcc:

In file included from boost_1_53_0/boost/type_traits/decay.hpp:18:0,
from boost_1_53_0/boost/filesystem/path_traits.hpp:22,
from boost_1_53_0/boost/filesystem/path.hpp:25,
from boost_1_53_0/boost/filesystem.hpp:16,
from ~/20130519_searchDatFiles/main.cpp:3:
boost_1_53_0/boost/mpl/eval_if.hpp: In instantiation of ‘boost::mpl::eval_if_c<false, boost::range_const_iterator<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>, boost::range_mutable_iterator<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())> >’:
boost_1_53_0/boost/range/iterator.hpp:63:63:   instantiated from ‘boost::range_iterator<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>’
boost_1_53_0/boost/range/concepts.hpp:256:72:   instantiated from ‘boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>’
boost_1_53_0/boost/concept/detail/has_constraints.hpp:42:5:   instantiated from ‘const bool boost::concepts::not_satisfied<boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())> >::value’
boost_1_53_0/boost/concept/detail/has_constraints.hpp:45:31:   instantiated from ‘boost::concepts::not_satisfied<boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())> >’
boost_1_53_0/boost/mpl/if.hpp:67:11:   instantiated from ‘boost::mpl::if_<boost::concepts::not_satisfied<boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())> >, boost::concepts::constraint<boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())> >, boost::concepts::requirement<boost::concepts::failed************ boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>::************> >’
boost_1_53_0/boost/concept/detail/general.hpp:50:8:   instantiated from ‘boost::concepts::requirement_<void (*)(boost::SinglePassRangeConcept<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>)>’
boost_1_53_0/boost/range/algorithm/copy.hpp:33:1:   instantiated from ‘OutputIterator boost::range::copy(const SinglePassRange&, OutputIterator) [with SinglePassRange = boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)()), OutputIterator = std::_List_iterator<boost::filesystem::directory_entry>]’
~/20130519_searchDatFiles/main.cpp:45:42:   instantiated from here
boost_1_53_0/boost/mpl/eval_if.hpp:60:31: error: no type named ‘type’ in ‘boost::mpl::eval_if_c<false, boost::range_const_iterator<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>, boost::range_mutable_iterator<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())> >::f_ {aka struct boost::range_mutable_iterator<boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)())>}’
In file included from boost_1_53_0/boost/range/algorithm.hpp:53:0,
from ~/20130519_searchDatFiles/main.cpp:7:
boost_1_53_0/boost/range/algorithm/copy.hpp: In function ‘OutputIterator boost::range::copy(const SinglePassRange&, OutputIterator) [with SinglePassRange = boost::iterator_range<boost::filesystem::directory_iterator>(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)()), OutputIterator = std::_List_iterator<boost::filesystem::directory_entry>]’:
~/20130519_searchDatFiles/main.cpp:45:42:   instantiated from here
boost_1_53_0/boost/range/algorithm/copy.hpp:34:59: error: no matching function for call to ‘begin(boost::iterator_range<boost::filesystem::directory_iterator> (&)(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)()))’
boost_1_53_0/boost/range/algorithm/copy.hpp:34:59: note: candidates are:
boost_1_53_0/boost/range/begin.hpp:101:55: note: template<class T> typename boost::range_iterator::type boost::range_adl_barrier::begin(T&)
boost_1_53_0/boost/range/begin.hpp:112:61: note: template<class T> typename boost::range_iterator<const T>::type boost::range_adl_barrier::begin(const T&)
boost_1_53_0/boost/range/algorithm/copy.hpp:34:59: error: no matching function for call to ‘end(boost::iterator_range<boost::filesystem::directory_iterator> (&)(boost::filesystem::directory_iterator, boost::filesystem::directory_iterator (*)()))’
boost_1_53_0/boost/range/algorithm/copy.hpp:34:59: note: candidates are:
boost_1_53_0/boost/range/end.hpp:95:55: note: template<class T> typename boost::range_iterator::type boost::range_adl_barrier::end(T&)
boost_1_53_0/boost/range/end.hpp:106:61: note: template<class T> typename boost::range_iterator<const T>::type boost::range_adl_barrier::end(const T&)
boost_1_53_0/boost/range/algorithm/copy.hpp:35:1: warning: control reaches end of non-void function [-Wreturn-type]

Я не смог отфильтровать соответствующие строки, но я думаю, что можно фильтровать файлы с помощью адаптеров диапазона (в конце концов, я не хочу просто копировать диапазон). У меня сложилось впечатление, что где-то по пути datFiles.begin() не соответствует концепции, необходимой для алгоритма копирования.


Некоторые из моих решений

Вот некоторые из решений (включая фильтрацию), которые я придумал, основываясь на ответе Джонатана.

Использование глобальной функции предиката bool isDatFile(const path& p):

std::list<directory_entry> datFiles;
boost::copy(rng | boost::adaptors::filtered(isDatFile), std::back_inserter(datFiles));

Я предпочел локальную функцию, поэтому я попробовал это. Этот просто проверяет, является ли запись каталога обычным файлом, статическое приведение было необходимо, потому что is_regular_file перегружен:

std::list<directory_entry> datFiles;
boost::copy(rng
| boost::adaptors::filtered(static_cast<bool (*)(const path&)>(&is_regular_file)),
std::back_inserter(datFiles));

Проверить правильность расширения в адаптере диапазона также было довольно сложно, поэтому я добавил локальную функцию:

std::list<directory_entry> datFiles;
bool BOOST_LOCAL_FUNCTION(const path& p)
{
return (is_regular_file(p) && (extension(p) == string(".dat")));
} BOOST_LOCAL_FUNCTION_NAME(isDatFile_l)
boost::copy(rng
| boost::adaptors::filtered(isDatFile_l),
std::back_inserter(datFiles));

И, наконец, «однострочник», использующий bind:

std::list<directory_entry> datFiles;
boost::copy(rng
| boost::adaptors::filtered(static_cast<bool (*)(const path&)>(&is_regular_file))
| boost::adaptors::filtered(boost::bind(std::equal_to<path>(), boost::bind(extension, _1), path(".dat"))),
std::back_inserter(datFiles));

3

Решение

Эта линия ударяется Most Vexing Parse:

boost::iterator_range<directory_iterator> rng(directory_iterator(p), directory_iterator());

Это объявляет функцию, а не объект.

Вы можете изменить это на:

boost::iterator_range<directory_iterator> rng = boost::iterator_range<directory_iterator>(directory_iterator(p), directory_iterator());

Или в C ++ 11:

auto rng = boost::iterator_range<directory_iterator>(directory_iterator(p), directory_iterator());

Но еще лучше использовать для этого заводскую функцию:

boost::iterator_range<directory_iterator> rng = boost::make_iterator_range(directory_iterator(p), directory_iterator());

Используя make_iterator_range вам не нужно называть тип итератора, он будет выведен из типов аргументов.

Другая проблема заключается в том, что вы пишете в пустой список, что является неопределенным поведением, вы должны использовать итератор вставки для вызова push_back() как в вашем рабочем коде. Собираем все вместе:

std::list<directory_entry> datFiles;
boost::copy( boost::make_iterator_range(directory_iterator(p), directory_iterator()),
std::back_inserter(datFiles) );

В C ++ 11 это более кратко:

std::list<directory_entry> datFiles;
boost::copy( boost::make_iterator_range(directory_iterator(p), {}),
std::back_inserter(datFiles) );

Теперь вы можете добавить фильтр довольно легко:

std::list<directory_entry> datFiles;
boost::copy( boost::make_iterator_range(directory_iterator(p), {}) | some_dir_filter,
std::back_inserter(datFiles) );
8

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]