2010-04-08 18 views
3

我有一箇中等複雜的迭代器,它將FindXFile apis封裝在Win32上。 (請參閱previous question)爲了避免構造基本上與WIN32_FIND_DATAW結構重複的對象的開銷,我有一個代理對象,它只是對WIN32_FIND_DATAW的一種常量引用,它在不可複製的內部迭代器。這是偉大的,因爲在C++迭代器中使用代理模式

  1. 客戶沒有爲他們可能不會用不相干的信息化建設(大多數時候人們只對文件名感興趣)支付,並
  2. 客戶端可以提供的所有信息得到如果他們需要或想要這些信息,則由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, &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

我不明白,爲什麼你沒有你的迭代產生包含WIN32_FIND_DATAW數據時,它的間接引用,而不是指的是被修改了這個代理對象的對象當迭代器增加時。

迭代器被標記爲在輸入迭代器中,讓用戶決定他們是否需要它引用或不引用的文件信息的副本。解引用迭代器應該返回一個可複製對象,其類型接口類包含類中適當的成員數據,因此可以根據需要複製該數據以維護對象的身份。這個類不需要指示如何執行查找操作的模板參數,因爲對象如果該類實際上與查找沒有任何關係,除了find操作可以產生它 - 此對象只包含有關一份文件。

然後,當迭代器被取消引用時,它可以返回該類型的對象。

當迭代器遞增時,它不需要與它在解除引用時返回的對象有任何關係(即使某些數據看起來非常像返回和/或使用的對象在增量操作中)。

我可能會重命名FileData<>類,你必須像FileFindDataInternal<>類,因爲它只是作爲迭代器內部操作的一部分使用。

這將釋放名稱FileData用於包裝用戶感興趣的信息的類,以及哪些應該可以由用戶複製。

+0

「爲什麼你沒有你的迭代器生成一個包含在它的取消引用的數據WIN32_FIND_DATAW的對象」 < - 因爲此後每解引用需要複製整個580字節WIN32_FIND_DATA結構,加上根路徑字符串。但是如果你認爲這是一個不可接受的折衷,那麼我會重構代理。 – 2010-04-08 22:35:36

+0

@邁克爾伯爾:編輯修改成我的問題。那是你所指的? – 2010-04-09 00:28:47