ejemplos - iteradores c++
¿Cómo podría envolver las API de FindXFile-style al Patrón de Iterator de estilo STL en C++? (6)
Estoy trabajando en cerrar las feas entrañas del bucle FindFirstFile
/ FindNextFile
(aunque mi pregunta se aplica a otras API similares, como RegEnumKeyEx
o RegEnumValue
, etc.) dentro de iteradores que funcionan de manera similar a istream_iterator
s de la biblioteca de plantillas estándar. .
Tengo dos problemas aquí. El primero es con la condición de terminación de la mayoría de los bucles de estilo "foreach". Los iteradores de estilo STL normalmente usan operator!=
Dentro de la condición de salida de for, es decir,
std::vector<int> test;
for(std::vector<int>::iterator it = test.begin(); it != test.end(); it++) {
//Do stuff
}
Mi problema es que no estoy seguro de cómo implementar operator!=
Con dicha enumeración de directorios, porque no sé cuándo se completará la enumeración hasta que haya terminado con ella. Tengo una solución tipo hackear juntos ahora que enumera todo el directorio a la vez, donde cada iterador simplemente rastrea un vector de referencia contado, pero esto parece un error que se puede hacer de una mejor manera.
El segundo problema que tengo es que hay múltiples piezas de datos devueltos por las API FindXFile. Por esa razón, no hay una forma obvia de sobrecargar al operator*
como se requiere para la semántica del iterador. Cuando sobrecargo ese artículo, ¿devuelvo el nombre del archivo? ¿El tamaño? La fecha modificada? ¿Cómo podría transmitir los múltiples datos a los que tal iterador debe hacer referencia más tarde de forma ideomática? Intenté MoveNext
diseño MoveNext
estilo C # pero me preocupa no seguir las expresiones estándar aquí.
class SomeIterator {
public:
bool next(); //Advances the iterator and returns true if successful, false if the iterator is at the end.
std::wstring fileName() const;
//other kinds of data....
};
EDITAR: Y la persona que llama se vería así:
SomeIterator x = ??; //Construct somehow
while(x.next()) {
//Do stuff
}
¡Gracias!
Billy3
EDIT2: he arreglado algunos errores y he escrito algunas pruebas.
Implementación:
#pragma once
#include <queue>
#include <string>
#include <boost/noncopyable.hpp>
#include <boost/make_shared.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include "../Exception.hpp"
namespace WindowsAPI { namespace FileSystem {
template <typename Filter_T = AllResults, typename Recurse_T = NonRecursiveEnumeration>
class DirectoryIterator;
//For unit testing
struct RealFindXFileFunctions
{
static HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
return FindFirstFile(lpFileName, lpFindFileData);
};
static BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) {
return FindNextFile(hFindFile, lpFindFileData);
};
static BOOL Close(HANDLE hFindFile) {
return FindClose(hFindFile);
};
};
inline std::wstring::const_iterator GetLastSlash(std::wstring const&pathSpec) {
return std::find(pathSpec.rbegin(), pathSpec.rend(), L''//').base();
}
class Win32FindData {
WIN32_FIND_DATA internalData;
std::wstring rootPath;
public:
Win32FindData(const std::wstring& root, const WIN32_FIND_DATA& data) :
rootPath(root), internalData(data) {};
DWORD GetAttributes() const {
return internalData.dwFileAttributes;
};
bool IsDirectory() const {
return (internalData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
};
bool IsFile() const {
return !IsDirectory();
};
unsigned __int64 GetSize() const {
ULARGE_INTEGER intValue;
intValue.LowPart = internalData.nFileSizeLow;
intValue.HighPart = internalData.nFileSizeHigh;
return intValue.QuadPart;
};
std::wstring GetFolderPath() const {
return rootPath;
};
std::wstring GetFileName() const {
return internalData.cFileName;
};
std::wstring GetFullFileName() const {
return rootPath + L"//" + internalData.cFileName;
};
std::wstring GetShortFileName() const {
return internalData.cAlternateFileName;
};
FILETIME GetCreationTime() const {
return internalData.ftCreationTime;
};
FILETIME GetLastAccessTime() const {
return internalData.ftLastAccessTime;
};
FILETIME GetLastWriteTime() const {
return internalData.ftLastWriteTime;
};
};
template <typename FindXFileFunctions_T>
class BasicNonRecursiveEnumeration : public boost::noncopyable
{
WIN32_FIND_DATAW currentData;
HANDLE hFind;
std::wstring currentDirectory;
void IncrementCurrentDirectory() {
if (hFind == INVALID_HANDLE_VALUE) return;
BOOL success =
FindXFileFunctions_T::FindNext(hFind, ¤tData);
if (success)
return;
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
FindXFileFunctions_T::Close(hFind);
hFind = INVALID_HANDLE_VALUE;
} else {
WindowsApiException::Throw(error);
}
};
bool IsValidDotDirectory()
{
return !Valid() &&
(!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L".."));
};
void IncrementPastDotDirectories() {
while (IsValidDotDirectory()) {
IncrementCurrentDirectory();
}
};
void PerformFindFirstFile(std::wstring const&pathSpec)
{
hFind = FindXFileFunctions_T::FindFirst(pathSpec.c_str(), ¤tData);
if (Valid()
&& GetLastError() != ERROR_PATH_NOT_FOUND
&& GetLastError() != ERROR_FILE_NOT_FOUND)
WindowsApiException::ThrowFromLastError();
};
public:
BasicNonRecursiveEnumeration() : hFind(INVALID_HANDLE_VALUE) {};
BasicNonRecursiveEnumeration(const std::wstring& pathSpec) :
hFind(INVALID_HANDLE_VALUE) {
std::wstring::const_iterator lastSlash = GetLastSlash(pathSpec);
if (lastSlash != pathSpec.begin())
currentDirectory.assign(pathSpec.begin(), lastSlash-1);
PerformFindFirstFile(pathSpec);
IncrementPastDotDirectories();
};
bool equal(const BasicNonRecursiveEnumeration<FindXFileFunctions_T>& other) const {
if (this == &other)
return true;
return hFind == other.hFind;
};
Win32FindData dereference() {
return Win32FindData(currentDirectory, currentData);
};
void increment() {
IncrementCurrentDirectory();
};
bool Valid() {
return hFind == INVALID_HANDLE_VALUE;
};
virtual ~BasicNonRecursiveEnumeration() {
if (!Valid())
FindXFileFunctions_T::Close(hFind);
};
};
typedef BasicNonRecursiveEnumeration<RealFindXFileFunctions> NonRecursiveEnumeration;
template <typename FindXFileFunctions_T>
class BasicRecursiveEnumeration : public boost::noncopyable
{
std::wstring fileSpec;
std::deque<std::deque<Win32FindData> > enumeratedData;
void EnumerateDirectory(const std::wstring& nextPathSpec) {
std::deque<Win32FindData> newDeck;
BasicNonRecursiveEnumeration<FindXFileFunctions_T> begin(nextPathSpec), end;
for(; !begin.equal(end); begin.increment()) {
newDeck.push_back(begin.dereference());
}
if (!newDeck.empty()) {
enumeratedData.push_back(std::deque<Win32FindData>()); //Swaptimization
enumeratedData.back().swap(newDeck);
}
};
void PerformIncrement() {
if (enumeratedData.empty()) return;
if (enumeratedData.back().front().IsDirectory()) {
std::wstring nextSpec(enumeratedData.back().front().GetFullFileName());
nextSpec.append(L"//*");
enumeratedData.back().pop_front();
EnumerateDirectory(nextSpec);
} else {
enumeratedData.back().pop_front();
}
while (Valid() && enumeratedData.back().empty())
enumeratedData.pop_back();
}
bool CurrentPositionNoMatchFileSpec() const
{
return !enumeratedData.empty() && !PathMatchSpecW(enumeratedData.back().front().GetFileName().c_str(), fileSpec.c_str());
}
public:
BasicRecursiveEnumeration() {};
BasicRecursiveEnumeration(const std::wstring& pathSpec) {
std::wstring::const_iterator lastSlash = GetLastSlash(pathSpec);
if (lastSlash == pathSpec.begin()) {
fileSpec = pathSpec;
EnumerateDirectory(L"*");
} else {
fileSpec.assign(lastSlash, pathSpec.end());
std::wstring firstQuery(pathSpec.begin(), lastSlash);
firstQuery.push_back(L''*'');
EnumerateDirectory(firstQuery);
while (CurrentPositionNoMatchFileSpec())
PerformIncrement();
}
};
void increment() {
do
{
PerformIncrement();
} while (CurrentPositionNoMatchFileSpec());
};
bool equal(const BasicRecursiveEnumeration<FindXFileFunctions_T>& other) const {
if (!Valid())
return !other.Valid();
if (!other.Valid())
return false;
return this == &other;
};
Win32FindData dereference() const {
return enumeratedData.back().front();
};
bool Valid() const {
return !enumeratedData.empty();
};
};
typedef BasicRecursiveEnumeration<RealFindXFileFunctions> RecursiveEnumeration;
struct AllResults
{
bool operator()(const Win32FindData&) {
return true;
};
};
struct FilesOnly
{
bool operator()(const Win32FindData& arg) {
return arg.IsFile();
};
};
template <typename Filter_T, typename Recurse_T>
class DirectoryIterator :
public boost::iterator_facade<
DirectoryIterator<Filter_T, Recurse_T>,
Win32FindData,
std::input_iterator_tag,
Win32FindData
>
{
friend class boost::iterator_core_access;
boost::shared_ptr<Recurse_T> impl;
Filter_T filter;
void increment() {
do {
impl->increment();
} while (impl->Valid() && !filter(impl->dereference()));
};
bool equal(const DirectoryIterator& other) const {
return impl->equal(*other.impl);
};
Win32FindData dereference() const {
return impl->dereference();
};
public:
DirectoryIterator(Filter_T functor = Filter_T()) :
impl(boost::make_shared<Recurse_T>()),
filter(functor) {
};
explicit DirectoryIterator(const std::wstring& pathSpec, Filter_T functor = Filter_T()) :
impl(boost::make_shared<Recurse_T>(pathSpec)),
filter(functor) {
};
};
}}
Pruebas:
#include <queue>
#include "../WideCharacterOutput.hpp"
#include <boost/test/unit_test.hpp>
#include "../../WindowsAPI++/FileSystem/Enumerator.hpp"
using namespace WindowsAPI::FileSystem;
struct SimpleFakeFindXFileFunctions
{
static std::deque<WIN32_FIND_DATAW> fakeData;
static std::wstring insertedFileName;
static HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
insertedFileName.assign(lpFileName);
if (fakeData.empty()) {
SetLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
*lpFindFileData = fakeData.front();
fakeData.pop_front();
return reinterpret_cast<HANDLE>(42);
};
static BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
if (fakeData.empty()) {
SetLastError(ERROR_NO_MORE_FILES);
return 0;
}
*lpFindFileData = fakeData.front();
fakeData.pop_front();
return 1;
};
static BOOL Close(HANDLE hFindFile) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
};
std::deque<WIN32_FIND_DATAW> SimpleFakeFindXFileFunctions::fakeData;
std::wstring SimpleFakeFindXFileFunctions::insertedFileName;
struct ErroneousFindXFileFunctionFirst
{
static HANDLE FindFirst(LPCWSTR, LPWIN32_FIND_DATAW) {
SetLastError(ERROR_ACCESS_DENIED);
return INVALID_HANDLE_VALUE;
};
static BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
static BOOL Close(HANDLE hFindFile) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
};
struct ErroneousFindXFileFunctionNext
{
static HANDLE FindFirst(LPCWSTR, LPWIN32_FIND_DATAW) {
return reinterpret_cast<HANDLE>(42);
};
static BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
};
static BOOL Close(HANDLE hFindFile) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
return 1;
};
};
struct DirectoryIteratorTestsFixture
{
typedef SimpleFakeFindXFileFunctions fakeFunctor;
DirectoryIteratorTestsFixture() {
WIN32_FIND_DATAW test;
wcscpy_s(test.cFileName, L".");
wcscpy_s(test.cAlternateFileName, L".");
test.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&test.ftCreationTime);
test.ftLastWriteTime = test.ftCreationTime;
test.ftLastAccessTime = test.ftCreationTime;
test.nFileSizeHigh = 0;
test.nFileSizeLow = 0;
fakeFunctor::fakeData.push_back(test);
wcscpy_s(test.cFileName, L"..");
wcscpy_s(test.cAlternateFileName, L"..");
fakeFunctor::fakeData.push_back(test);
wcscpy_s(test.cFileName, L"File.txt");
wcscpy_s(test.cAlternateFileName, L"FILE.TXT");
test.nFileSizeLow = 1024;
test.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
fakeFunctor::fakeData.push_back(test);
wcscpy_s(test.cFileName, L"System32");
wcscpy_s(test.cAlternateFileName, L"SYSTEM32");
test.nFileSizeLow = 0;
test.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
fakeFunctor::fakeData.push_back(test);
};
~DirectoryIteratorTestsFixture() {
fakeFunctor::fakeData.clear();
};
};
BOOST_FIXTURE_TEST_SUITE( DirectoryIteratorTests, DirectoryIteratorTestsFixture )
template<typename fakeFunctor>
static void NonRecursiveIteratorAssertions()
{
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C://Windows//*");
testType end;
BOOST_CHECK_EQUAL(fakeFunctor::insertedFileName, L"C://Windows//*");
BOOST_CHECK(begin->GetFolderPath() == L"C://Windows");
BOOST_CHECK(begin->GetFileName() == L"File.txt");
BOOST_CHECK(begin->GetFullFileName() == L"C://Windows//File.txt");
BOOST_CHECK(begin->GetShortFileName() == L"FILE.TXT");
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK(begin->IsFile());
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->GetFileName() == L"System32");
BOOST_CHECK(begin->GetFullFileName() == L"C://Windows//System32");
BOOST_CHECK(begin->GetShortFileName() == L"SYSTEM32");
BOOST_CHECK_EQUAL(begin->GetSize(), 0);
BOOST_CHECK(begin->IsDirectory());
begin++;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( BasicEnumeration )
{
NonRecursiveIteratorAssertions<fakeFunctor>();
}
BOOST_AUTO_TEST_CASE( NoRootDirectories )
{
fakeFunctor::fakeData.pop_front();
fakeFunctor::fakeData.pop_front();
NonRecursiveIteratorAssertions<fakeFunctor>();
}
static void EmptyIteratorAssertions() {
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C://Windows//*");
testType end;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( Empty1 )
{
fakeFunctor::fakeData.clear();
EmptyIteratorAssertions();
}
BOOST_AUTO_TEST_CASE( Empty2 )
{
fakeFunctor::fakeData.erase(fakeFunctor::fakeData.begin() + 2, fakeFunctor::fakeData.end());
EmptyIteratorAssertions();
}
BOOST_AUTO_TEST_CASE( CorrectDestruction )
{
typedef DirectoryIterator<AllResults
,BasicNonRecursiveEnumeration<SimpleFakeFindXFileFunctions> > testType;
testType begin(L"C://Windows//*");
testType end;
}
BOOST_AUTO_TEST_CASE( Exceptions )
{
typedef DirectoryIterator<AllResults,BasicNonRecursiveEnumeration<ErroneousFindXFileFunctionFirst> >
firstFailType;
BOOST_CHECK_THROW(firstFailType(L"C://Windows//*"), WindowsAPI::ErrorAccessDeniedException);
typedef DirectoryIterator<AllResults,BasicNonRecursiveEnumeration<ErroneousFindXFileFunctionNext> >
nextFailType;
nextFailType constructedOkay(L"C://Windows//*");
BOOST_CHECK_THROW(constructedOkay++, WindowsAPI::ErrorInvalidParameterException);
}
BOOST_AUTO_TEST_SUITE_END()
struct RecursiveFakeFindXFileFunctions
{
static std::deque<std::pair<std::deque<WIN32_FIND_DATA> , std::wstring> > fakeData;
static std::size_t openHandles;
static HANDLE FindFirst(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) {
BOOST_REQUIRE(!fakeData.empty());
BOOST_REQUIRE_EQUAL(lpFileName, fakeData.front().second);
openHandles++;
BOOST_REQUIRE_EQUAL(openHandles, 1);
if (fakeData.front().first.empty()) {
openHandles--;
SetLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
*lpFindFileData = fakeData.front().first.front();
fakeData.front().first.pop_front();
return reinterpret_cast<HANDLE>(42);
};
static BOOL FindNext(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
if (fakeData.front().first.empty()) {
SetLastError(ERROR_NO_MORE_FILES);
return 0;
}
*lpFindFileData = fakeData.front().first.front();
fakeData.front().first.pop_front();
return 1;
};
static BOOL Close(HANDLE hFindFile) {
BOOST_CHECK_EQUAL(reinterpret_cast<HANDLE>(42), hFindFile);
openHandles--;
BOOST_REQUIRE_EQUAL(openHandles, 0);
fakeData.pop_front();
return 1;
};
};
std::deque<std::pair<std::deque<WIN32_FIND_DATA> , std::wstring> > RecursiveFakeFindXFileFunctions::fakeData;
std::size_t RecursiveFakeFindXFileFunctions::openHandles;
struct RecursiveDirectoryFixture
{
RecursiveDirectoryFixture() {
WIN32_FIND_DATAW tempData;
ZeroMemory(&tempData, sizeof(tempData));
std::deque<WIN32_FIND_DATAW> dequeData;
wcscpy_s(tempData.cFileName, L".");
wcscpy_s(tempData.cAlternateFileName, L".");
tempData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&tempData.ftCreationTime);
tempData.ftLastWriteTime = tempData.ftCreationTime;
tempData.ftLastAccessTime = tempData.ftCreationTime;
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"..");
wcscpy_s(tempData.cAlternateFileName, L"..");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"MySubDirectory");
wcscpy_s(tempData.cAlternateFileName, L"MYSUBD~1");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"MyFile.txt");
wcscpy_s(tempData.cAlternateFileName, L"MYFILE.TXT");
tempData.nFileSizeLow = 500;
tempData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
dequeData.push_back(tempData);
RecursiveFakeFindXFileFunctions::fakeData.push_back
(std::make_pair(dequeData, L"C://Windows//*"));
dequeData.clear();
wcscpy_s(tempData.cFileName, L".");
wcscpy_s(tempData.cAlternateFileName, L".");
tempData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&tempData.ftCreationTime);
tempData.ftLastWriteTime = tempData.ftCreationTime;
tempData.ftLastAccessTime = tempData.ftCreationTime;
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"..");
wcscpy_s(tempData.cAlternateFileName, L"..");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"MyFile2.txt");
wcscpy_s(tempData.cAlternateFileName, L"NYFILE2.TXT");
tempData.nFileSizeLow = 1024;
tempData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
dequeData.push_back(tempData);
RecursiveFakeFindXFileFunctions::fakeData.push_back
(std::make_pair(dequeData, L"C://Windows//MySubDirectory//*"));
};
~RecursiveDirectoryFixture() {
RecursiveFakeFindXFileFunctions::fakeData.clear();
};
};
BOOST_AUTO_TEST_SUITE( RecursiveDirectoryIteratorTests )
BOOST_AUTO_TEST_CASE( BasicEnumerationTxt )
{
RecursiveDirectoryFixture DataFixture;
typedef DirectoryIterator<AllResults
,BasicRecursiveEnumeration<RecursiveFakeFindXFileFunctions> > testType;
testType begin(L"C://Windows//*.txt");
testType end;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows//MySubDirectory");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MyFile2.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MySubDirectory//MyFile2.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 500);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MyFile.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MyFile.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( BasicEnumerationAll )
{
RecursiveDirectoryFixture DataFixture;
typedef DirectoryIterator<AllResults
,BasicRecursiveEnumeration<RecursiveFakeFindXFileFunctions> > testType;
testType begin(L"C://Windows//*");
testType end;
BOOST_CHECK(begin->IsDirectory());
BOOST_CHECK_EQUAL(begin->GetSize(), 0);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MySubDirectory");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MySubDirectory");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows//MySubDirectory");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MyFile2.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MySubDirectory//MyFile2.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 500);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MyFile.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MyFile.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( RecursionOrderMaintained )
{
WIN32_FIND_DATAW tempData;
ZeroMemory(&tempData, sizeof(tempData));
std::deque<WIN32_FIND_DATAW> dequeData;
wcscpy_s(tempData.cFileName, L".");
wcscpy_s(tempData.cAlternateFileName, L".");
tempData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&tempData.ftCreationTime);
tempData.ftLastWriteTime = tempData.ftCreationTime;
tempData.ftLastAccessTime = tempData.ftCreationTime;
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"..");
wcscpy_s(tempData.cAlternateFileName, L"..");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"MySubDirectory");
wcscpy_s(tempData.cAlternateFileName, L"MYSUBD~1");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"MyFile.txt");
wcscpy_s(tempData.cAlternateFileName, L"MYFILE.TXT");
tempData.nFileSizeLow = 500;
tempData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"Zach");
wcscpy_s(tempData.cAlternateFileName, L"ZACH");
tempData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
tempData.nFileSizeLow = 0;
dequeData.push_back(tempData);
RecursiveFakeFindXFileFunctions::fakeData.push_back
(std::make_pair(dequeData, L"C://Windows//*"));
dequeData.clear();
wcscpy_s(tempData.cFileName, L".");
wcscpy_s(tempData.cAlternateFileName, L".");
tempData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&tempData.ftCreationTime);
tempData.ftLastWriteTime = tempData.ftCreationTime;
tempData.ftLastAccessTime = tempData.ftCreationTime;
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"..");
wcscpy_s(tempData.cAlternateFileName, L"..");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"MyFile2.txt");
wcscpy_s(tempData.cAlternateFileName, L"NYFILE2.TXT");
tempData.nFileSizeLow = 1024;
tempData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
dequeData.push_back(tempData);
RecursiveFakeFindXFileFunctions::fakeData.push_back
(std::make_pair(dequeData, L"C://Windows//MySubDirectory//*"));
dequeData.clear();
wcscpy_s(tempData.cFileName, L".");
wcscpy_s(tempData.cAlternateFileName, L".");
tempData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
GetSystemTimeAsFileTime(&tempData.ftCreationTime);
tempData.ftLastWriteTime = tempData.ftCreationTime;
tempData.ftLastAccessTime = tempData.ftCreationTime;
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"..");
wcscpy_s(tempData.cAlternateFileName, L"..");
dequeData.push_back(tempData);
wcscpy_s(tempData.cFileName, L"ZachFile.txt");
wcscpy_s(tempData.cAlternateFileName, L"ZACHFILE.TXT");
tempData.nFileSizeLow = 1024;
tempData.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
dequeData.push_back(tempData);
RecursiveFakeFindXFileFunctions::fakeData.push_back
(std::make_pair(dequeData, L"C://Windows//Zach//*"));
typedef DirectoryIterator<AllResults
,BasicRecursiveEnumeration<RecursiveFakeFindXFileFunctions> > testType;
testType begin(L"C://Windows//*");
testType end;
BOOST_CHECK(begin->IsDirectory());
BOOST_CHECK_EQUAL(begin->GetSize(), 0);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MySubDirectory");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MySubDirectory");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows//MySubDirectory");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MyFile2.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MySubDirectory//MyFile2.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 500);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"MyFile.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//MyFile.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsDirectory());
BOOST_CHECK_EQUAL(begin->GetSize(), 0);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"Zach");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//Zach");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin->IsFile());
BOOST_CHECK_EQUAL(begin->GetSize(), 1024);
BOOST_CHECK_EQUAL(begin->GetFolderPath(), L"C://Windows//Zach");
BOOST_CHECK_EQUAL(begin->GetFileName(), L"ZachFile.txt");
BOOST_CHECK_EQUAL(begin->GetFullFileName(), L"C://Windows//Zach//ZachFile.txt");
BOOST_CHECK(begin != end);
begin++;
BOOST_CHECK(begin == end);
}
BOOST_AUTO_TEST_CASE( Exceptions )
{
typedef DirectoryIterator<AllResults,BasicRecursiveEnumeration<ErroneousFindXFileFunctionFirst> >
firstFailType;
BOOST_CHECK_THROW(firstFailType(L"C://Windows//*"), WindowsAPI::ErrorAccessDeniedException);
typedef DirectoryIterator<AllResults,BasicRecursiveEnumeration<ErroneousFindXFileFunctionNext> >
nextFailType;
BOOST_CHECK_THROW(nextFailType(L"C://Windows//*"), WindowsAPI::ErrorInvalidParameterException);
}
BOOST_AUTO_TEST_SUITE_END()
Curiosamente, tuve la misma idea hace algún tiempo. Esto es lo que escribí:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string>
#include <iterator>
#include <exception>
#ifndef DIR_ITERATOR_H_INC
#define DIR_ITERATOR_H_INC
class dir_iterator
#if (!defined(_MSC_VER)) || (_MSC_VER > 1200)
: public std::iterator<std::input_iterator_tag,
std::string,
int,
std::string *,
std::string &>
#endif
{
mutable HANDLE it;
std::string mask;
std::string path;
WIN32_FIND_DATA data;
bool done;
DWORD require;
DWORD prohibit;
public:
WIN32_FIND_DATA operator*() {
return data;
}
dir_iterator(dir_iterator const &other) :
it(other.it),
mask(other.mask),
path(other.path),
data(other.data),
done(other.done),
require(other.require),
prohibit(other.prohibit)
{
// Transfer the handle instead of just copying it.
other.it=INVALID_HANDLE_VALUE;
}
dir_iterator(std::string const &s,
DWORD must = 0,
DWORD cant = FILE_ATTRIBUTE_DIRECTORY)
: mask(s),
require(must),
prohibit(cant & ~must),
done(false),
it(INVALID_HANDLE_VALUE) // To fix bug spotted by Billy ONeal.
{
int pos;
if (std::string::npos != (pos=mask.find_last_of("///")))
path = std::string(mask, 0, pos+1);
it = FindFirstFile(mask.c_str(), &data);
if (it == INVALID_HANDLE_VALUE)
throw std::invalid_argument("Directory Inaccessible");
while (!(((data.dwFileAttributes & require) == require) &&
((data.dwFileAttributes & prohibit) == 0)))
{
if (done = (FindNextFile(it, &data)==0))
break;
}
}
dir_iterator() : done(true) {}
dir_iterator &operator++() {
do {
if (done = (FindNextFile(it, &data)==0))
break;
} while (!(((data.dwFileAttributes & require) == require) &&
(data.dwFileAttributes & prohibit) == 0));
return *this;
}
bool operator!=(dir_iterator const &other) {
return done != other.done;
}
bool operator==(dir_iterator const &other) {
return done == other.done;
}
~dir_iterator() {
// The rest of the bug fix -- only close handle if it''s open.
if (it!=INVALID_HANDLE_VALUE)
FindClose(it);
}
};
#endif
Y una demostración rápida de esto:
#include "dir_iterator.h"
#include <iostream>
#include <algorithm>
namespace std {
std::ostream &operator<<(std::ostream &os, WIN32_FIND_DATA const &d) {
return os << d.cFileName;
}
}
int main() {
std::copy(dir_iterator("*"), dir_iterator(),
std::ostream_iterator<WIN32_FIND_DATA>(std::cout, "/n"));
std::cout << "/nDirectories:/n";
std::copy(dir_iterator("*", FILE_ATTRIBUTE_DIRECTORY), dir_iterator(),
std::ostream_iterator<WIN32_FIND_DATA>(std::cout, "/n"));
return 0;
}
Matthew Wilson ha escrito varios artículos sobre cómo adaptar la enumeración de directorios a los iteradores de STL. Desafortunadamente, el artículo que aborda directamente las API de Windows ya no parece estar en línea. Sin embargo, me imagino que las ideas que se discuten en algunos de sus otros artículos probablemente todavía sean muy relevantes, y también proporciona una biblioteca de código abierto (WinSTL - http://winstl.org/ ) con la implementación de Windows.
- Búsquedas de comodines en directorios de UNIX con iteradores de acceso aleatorio
- Lectura de directorios UNIX a través de secuencias compatibles con STL
Además, estoy seguro de que la fuente y la documentación de Boost :: Filesystem son una gran fuente de ideas.
Para resolver el primer problema, puede hacer que end()
devuelva algún valor centinela, luego en la función de incremento de su iterador, establezca el iterador igual a ese valor centinela cuando llegue al final del contenedor. Esto es efectivamente lo que hace el iterador de directorio en Boost.Filesystem.
Para el segundo problema, no estoy completamente familiarizado con las API FindXFile
, pero una opción sería devolver alguna estructura que contenga todos los datos que necesita o que tenga funciones miembro para obtener cada uno de los datos que pueda desear.
A menos que lo estés haciendo para aprender, mi respuesta sería: no, ya está hecho. Las bibliotecas de STLSoft contienen la plantilla de clase winstl :: basic_findfile_sequence que maneja la mayoría de los casos de uso para buscar en un directorio de Windows, incluidos comodines, patrones de varias partes (por ejemplo, " .xls | .doc"), puntos de reanálisis y más. Está documentado en gran detalle en el extenso tratado de Matthew Wilson sobre la extensión STL "Extended STL, volume 1: Collections and Iterators". El libro es una lectura importante, pero contiene todas las cosas que querrá saber (y muchas otras que no) sobre cómo escribir extensiones STL.
Si necesita una búsqueda recursiva, considere la biblioteca recls (otra de Wilson), que también proporciona una interfaz STL, a través de la clase recls :: search_sequence. Vea aquí un grupo de ejemplos de un artículo reciente al respecto .
Buscar en la biblioteca recls, discutido aquí . (Es también disponible para .NET , fwiw).
Boost.Filesystem tiene STL plataforma independiente como iteradores. Tenga en cuenta sin embargo que los iteradores puede lanzar si un archivo es inaccesible.
Ejemplo (se puede pimped usando count_if y lambda):
namespace fs = boost::filesystem;
size_t nTotal = 0;
try
{
const fs::path pth("c://temp");
//default construction yields past-the-end
fs::recursive_directory_iterator itEnd;
for (fs::recursive_directory_iterator it(pth); it != itEnd; ++it)
{
const fs::directory_entry& rEntry = *it;
if (fs::is_regular_file(rEntry))
{
nTotal += fs::file_size(rEntry);
}
}
}
catch (const fs::filesystem_error& re)
{
//when directoy or file is not found
}