gpt4 book ai didi

c++ - 将代理模式与 C++ 迭代器一起使用

转载 作者:太空狗 更新时间:2023-10-29 23:10:02 25 4
gpt4 key购买 nike

我编写了一个中等复杂的迭代器,它包装了 Win32 上的 FindXFile api。 (参见 previous question)为了避免构造一个本质上重复 WIN32_FIND_DATAW 结构工作的对象的开销,我有一个代理对象,它只是作为对单个 WIN32_FIND_DATAW 的常量引用,它在不可复制的内部声明迭代器的内部结构。这很棒,因为

  1. 客户不会为构建他们可能不会使用的无关信息付费(大多数时候人们只对文件名感兴趣),并且
  2. 如果客户需要或想要这些信息,他们可以获得 FindXFile API 提供的所有信息。

但这成为一个问题,因为对象的实际数据只有一个拷贝。因此,当迭代器递增时,所有代理都将失效(设置为迭代器指向的下一个文件是什么)。

我担心这是否是一个主要问题,因为我可以想到代理对象不会像某些人期望的那样表现的情况:

std::vector<MyIterator::value_type> files;
std::copy(MyIterator("Hello"), MyIterator(), std::back_inserter(files));

因为此时 vector 仅包含一堆无效代理。相反,客户需要做类似的事情:

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, &currentData);
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(), &currentData);
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(), &currentData);
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 的对象数据在取消引用时引用,而不是引用在迭代器递增时被修改的代理对象。

迭代器被标记为输入迭代器,让用户决定他们是否想要它所引用的那个文件信息的拷贝。取消引用迭代器应该返回一个可复制的对象,其接口(interface)与您的 FileData 相似。包含适当成员数据的类,因此可以根据需要复制这些成员数据以维护对象的身份。此类不需要指示应如何执行查找操作的模板参数,因为如果该类的对象实际上与查找没有任何关系,除了查找操作可以产生它 - 此对象仅包含有关的信息一个文件。

然后当迭代器被取消引用时,它可以返回该类型的对象。

迭代器在递增时所做的事情不需要与它在取消引用时返回的对象有任何关系(即使其中一些数据看起来非常像返回和/或在增量操作)。

我可能会重命名 FileData<>类你必须像FileFindDataInternal<> ,因为它实际上仅用作迭代器内部操作的一部分。

这将释放名称 FileData用于包装用户感兴趣的信息的类,并且用户应该可以复制它。

关于c++ - 将代理模式与 C++ 迭代器一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2603483/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com