我有一箇中等複雜的迭代器,它將FindXFile apis封裝在Win32上。 (請參閱previous question)爲了避免構造基本上與WIN32_FIND_DATAW結構重複的對象的開銷,我有一個代理對象,它只是對WIN32_FIND_DATAW的一種常量引用,它在不可複製的內部迭代器。這是偉大的,因爲在C++迭代器中使用代理模式
- 客戶沒有爲他們可能不會用不相干的信息化建設(大多數時候人們只對文件名感興趣)支付,並
- 客戶端可以提供的所有信息得到如果他們需要或想要這些信息,則由FindXFile API提供。
這成爲一個問題,因爲只有對象的實際數據的一個副本。因此,迭代器增量時,所有代理都將失效(設置爲迭代器指向的下一個文件)。
我很擔心,如果這是一個大問題,因爲我能想到的地方代理對象不會表現的情況下,作爲有人會想到:
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) {
};
};
}}
「爲什麼你沒有你的迭代器生成一個包含在它的取消引用的數據WIN32_FIND_DATAW的對象」 < - 因爲此後每解引用需要複製整個580字節WIN32_FIND_DATA結構,加上根路徑字符串。但是如果你認爲這是一個不可接受的折衷,那麼我會重構代理。 – 2010-04-08 22:35:36
@邁克爾伯爾:編輯修改成我的問題。那是你所指的? – 2010-04-09 00:28:47