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

时间:2010-04-08 21:09:46

标签: c++ winapi proxy iterator

我编写了一个中等复杂的迭代器,它将WinX上的FindXFile apis包装起来。 (参见previous question)为了避免构造一个基本上复制WIN32_FIND_DATAW结构工作的对象的开销,我有一个代理对象,它只是作为对单个WIN32_FIND_DATAW的一种const引用,它在内部声明迭代器的不可复制内部。这很好,因为

  1. 客户不支付他们可能不会使用的无关信息的构建费用(大多数时候人们只对文件名感兴趣),
  2. 如果需要或想要此信息,客户可以获取FindXFile API提供的所有信息。
  3. 这成为一个问题,因为对象的实际数据只有一个副本。因此,当迭代器增加时,所有代理都无效(设置为迭代器指向的下一个文件)。

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

    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, &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) {
        };
    };
    
    }}
    

1 个答案:

答案 0 :(得分:1)

我不明白为什么你没有你的迭代器产生一个包含WIN32_FIND_DATAW数据的对象,当它被取消引用时,而不是引用在迭代器递增时被修改的这个代理对象。

迭代器标记为输入迭代器,让用户决定是否需要它引用的文件信息的副本。取消引用迭代器应返回一个可复制对象,该对象具有与FileData类类似的接口,该类包含类中的相应成员数据,因此可以根据需要进行复制以维护对象的标识。这个类不需要指示如何执行查找操作的模板参数,因为对象如果该类与查找没有任何关系,除了查找操作可以生成它 - 该对象只包含有关的信息一个文件。

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

迭代器在递增时所执行的操作不需要与它在解除引用时返回的对象有任何关系(即使某些数据看起来非常像返回的对象和/或用于增量操作)。

我可能会将FileData<>类重命名为FileFindDataInternal<>,因为它只是作为迭代器内部操作的一部分使用。

这将释放名称FileData以用于包装用户感兴趣的信息的类,以及哪些应该由用户复制。

相关问题