У меня написан довольно сложный итератор, который оборачивает API FindXFile на Win32. (См. предыдущий вопрос ). Чтобы избежать накладных расходов на создание объекта, который по существу дублирует работу структуры WIN32_FIND_DATAW, у меня есть прокси-объект, который просто действует как своего рода константная ссылка на один WIN32_FIND_DATAW, который объявляется внутри некопируемых внутренностей итератора. Это здорово, потому что
- Клиенты не платят за создание нерелевантной информации, которую они, вероятно, не будут использовать (большую часть времени люди интересуются только именами файлов), а
- Клиенты могут получать всю информацию, предоставляемую API FindXFile, если им нужна или требуется эта информация.
Это становится проблемой, потому что существует только одна копия фактических данных объекта. Поэтому при увеличении итератора все прокси становятся недействительными (устанавливается равным следующему файлу, на который указывает итератор).
Я обеспокоен, если это серьезная проблема, потому что я могу вспомнить случай, когда прокси-объект не будет вести себя так, как кто-то может ожидать:
std::vector<MyIterator::value_type> files;
std::copy(MyIterator("Hello"), MyIterator(), std::back_inserter(files));
потому что вектор содержит только недопустимые прокси в этой точке. Вместо этого клиенты должны сделать что-то вроде:
std::vector<std::wstring> filesToSearch;
std::transform(
DirectoryIterator<FilesOnly>(L"C:\\Windows\\*"),
DirectoryIterator<FilesOnly>(),
std::back_inserter(filesToSearch),
std::mem_fun_ref(&DirectoryIterator<FilesOnly>::value_type::GetFullFileName)
);
Видя это, я понимаю, почему кому-то может не нравиться то, что дизайнеры стандартной библиотеки сделали с std::vector<bool>
. Я все еще задаюсь вопросом: это разумный компромисс для достижения (1) и (2) выше? Если нет, есть ли способ достичь (1) и (2) без прокси?
РЕДАКТИРОВАТЬ: Обновленный код:
#pragma once
#include <list>
#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 {
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;
};
};
class EnumerationMethod : public boost::noncopyable {
protected:
WIN32_FIND_DATAW currentData;
HANDLE hFind;
std::wstring currentDirectory;
EnumerationMethod() : hFind(INVALID_HANDLE_VALUE) {};
void IncrementCurrentDirectory() {
if (hFind == INVALID_HANDLE_VALUE) return;
BOOL success =
FindNextFile(hFind, ¤tData);
if (success)
return;
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
} else {
WindowsApiException::Throw(error);
}
};
virtual ~EnumerationMethod() {
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
};
public:
bool equal(const EnumerationMethod& other) const {
if (this == &other)
return true;
return hFind == other.hFind;
};
Win32FindData dereference() {
return Win32FindData(currentDirectory, currentData);
};
};
class NonRecursiveEnumeration : public EnumerationMethod
{
public:
NonRecursiveEnumeration() {};
NonRecursiveEnumeration(const std::wstring& pathSpec) {
std::wstring::const_iterator lastSlash =
std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base();
if (lastSlash != pathSpec.begin())
currentDirectory.assign(pathSpec.begin(), lastSlash-1);
hFind = FindFirstFileW(pathSpec.c_str(), ¤tData);
if (hFind == INVALID_HANDLE_VALUE)
WindowsApiException::ThrowFromLastError();
while (hFind != INVALID_HANDLE_VALUE && (!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L".."))) {
IncrementCurrentDirectory();
}
};
void increment() {
IncrementCurrentDirectory();
};
};
class RecursiveEnumeration : public EnumerationMethod
{
std::wstring fileSpec;
std::list<std::wstring> futureDirectories;
std::list<std::wstring>::iterator directoryInsertLocation;
void ShiftToNextDirectory() {
if (futureDirectories.empty()) {
hFind = INVALID_HANDLE_VALUE;
return;
}
//Get the next directory
currentDirectory = futureDirectories.front();
futureDirectories.pop_front();
directoryInsertLocation = futureDirectories.begin();
std::wstring pathSpec(currentDirectory);
if (!pathSpec.empty())
pathSpec.push_back(L'\\');
pathSpec.append(fileSpec);
hFind = FindFirstFileW(pathSpec.c_str(), ¤tData);
if (hFind == INVALID_HANDLE_VALUE)
WindowsApiException::ThrowFromLastError();
while (!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L"..")) {
IncrementCurrentDirectory();
}
};
void IncrementAndShift() {
if (hFind != INVALID_HANDLE_VALUE && (currentData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
directoryInsertLocation = futureDirectories.insert(directoryInsertLocation,
currentDirectory + L"\\" + currentData.cFileName);
directoryInsertLocation++;
}
IncrementCurrentDirectory();
if (hFind == INVALID_HANDLE_VALUE)
ShiftToNextDirectory();
};
public:
RecursiveEnumeration() {};
RecursiveEnumeration(const std::wstring& pathSpec) {
std::wstring::const_iterator lastSlash =
std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base();
if (lastSlash != pathSpec.begin()) {
futureDirectories.push_back(std::wstring(pathSpec.begin(), lastSlash-1));
fileSpec.assign(lastSlash, pathSpec.end());
} else {
futureDirectories.push_back(std::wstring());
fileSpec = pathSpec;
}
ShiftToNextDirectory();
};
void increment() {
do {
IncrementAndShift();
} while (!PathMatchSpecW(currentData.cFileName, fileSpec.c_str()));
};
};
struct AllResults
{
bool operator()(const Win32FindData&) {
return true;
};
};
struct FilesOnly
{
bool operator()(const Win32FindData& arg) {
return arg.IsFile();
};
};
template <typename Filter_T = AllResults, typename Recurse_T = NonRecursiveEnumeration>
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 (! 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) {
};
};
}}