ฉันมีตัววนซ้ำที่ซับซ้อนปานกลางซึ่งเขียนล้อม FindXFile apis บน Win32 (ดู คำถามก่อนหน้า) เพื่อหลีกเลี่ยงค่าใช้จ่ายในการสร้างวัตถุที่ทำซ้ำการทำงานของโครงสร้าง WIN32_FIND_DATAW เป็นหลัก ฉันมีวัตถุพร็อกซีซึ่งทำหน้าที่เป็นประเภทการอ้างอิง const ไปยัง WIN32_FIND_DATAW เดียวซึ่งได้รับการประกาศ ภายในอวัยวะภายในที่ไม่สามารถคัดลอกได้ของตัววนซ้ำ เรื่องนี้เยี่ยมมากเพราะว่า
- ลูกค้าไม่ต้องจ่ายเงินสำหรับการสร้างข้อมูลที่ไม่เกี่ยวข้องที่พวกเขาอาจจะไม่ได้ใช้ (ส่วนใหญ่ผู้คนจะสนใจเพียงชื่อไฟล์เท่านั้น) และ
- ลูกค้าสามารถรับข้อมูลทั้งหมดที่ได้รับจาก 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) {
};
};
}}