2010-04-07 56 views
5

看來我不得不在這裏插入相當多的代碼。我想知道,如果它是壞的設計實踐離開這完全是在這樣的一個頭文件:這是隻針對標題庫的代碼太多嗎?

#include <list> 
#include <string> 
#include <boost/noncopyable.hpp> 
#include <boost/make_shared.hpp> 
#include <boost/iterator/iterator_facade.hpp> 
#include <Windows.h> 
#include "../Exception.hpp" 

namespace WindowsAPI { namespace FileSystem { 

class NonRecursiveEnumeration; 
class RecursiveEnumeration; 
struct AllResults; 
struct FilesOnly; 

template <typename Filter_T = AllResults, typename Recurse_T = NonRecursiveEnumeration> 
class DirectoryIterator; 

template <typename Recurse_T> 
struct FileData; 

class NonRecursiveEnumeration : public boost::noncopyable 
{ 
    WIN32_FIND_DATAW currentData; 
    HANDLE hFind; 
    std::wstring root; 
public: 
    NonRecursiveEnumeration() : hFind(INVALID_HANDLE_VALUE) { 
    }; 
    NonRecursiveEnumeration(const std::wstring& pathSpec) { 
     std::wstring::const_iterator lastSlash = 
      std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base(); 
     if (lastSlash != pathSpec.end()) 
      root.assign(pathSpec.begin(), lastSlash); 
     hFind = FindFirstFileW(pathSpec.c_str(), &currentData); 
     if (hFind == INVALID_HANDLE_VALUE) 
      WindowsApiException::ThrowFromLastError(); 
     while (!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L"..")) { 
      increment(); 
     } 
    }; 
    void increment() { 
     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); 
     } 
    }; 
    ~NonRecursiveEnumeration() { 
     if (hFind != INVALID_HANDLE_VALUE) 
      FindClose(hFind); 
    }; 
    bool equal(const NonRecursiveEnumeration& other) const { 
     if (this == &other) 
      return true; 
     return hFind == other.hFind; 
    }; 
    const std::wstring& GetPathRoot() const { 
     return root; 
    }; 
    const WIN32_FIND_DATAW& GetCurrentFindData() const { 
     return currentData; 
    }; 
}; 

//Not implemented yet 
class RecursiveEnumeration : public boost::noncopyable 
{ 
}; 

template <typename Recurse_T> 
struct FileData //Serves as a proxy to the WIN32_FIND_DATA struture inside the iterator. 
{ 
    const Recurse_T* impl; 
    template <typename Filter_T, typename Recurse_T> 
    FileData(const DirectoryIterator<Filter_T, Recurse_T>* parent) : impl(parent->impl.get()) {}; 
    DWORD GetAttributes() const { 
     return impl->GetCurrentFindData().dwFileAttributes; 
    }; 
    bool IsDirectory() const { 
     return (GetAttributes() & FILE_ATTRIBUTE_DIRECTORY) != 0; 
    }; 
    bool IsFile() const { 
     return !IsDirectory(); 
    }; 
    bool IsArchive() const { 
     return (GetAttributes() & FILE_ATTRIBUTE_ARCHIVE) != 0; 
    }; 
    bool IsReadOnly() const { 
     return (GetAttributes() & FILE_ATTRIBUTE_READONLY) != 0; 
    }; 
    unsigned __int64 GetSize() const { 
     ULARGE_INTEGER intValue; 
     intValue.LowPart = impl.GetCurrentFindData().nFileSizeLow; 
     intValue.HighPart = impl.GetCurrentFindData().nFileSizeHigh; 
     return intValue.QuadPart; 
    }; 
    std::wstring GetFolderPath() const { 
     return impl->GetPathRoot(); 
    }; 
    std::wstring GetFileName() const { 
     return impl->GetCurrentFindData().cFileName; 
    }; 
    std::wstring GetFullFileName() const { 
     return GetFolderPath() + GetFileName(); 
    }; 
    std::wstring GetShortFileName() const { 
     return impl->GetCurrentFindData().cAlternateFileName; 
    }; 
    FILETIME GetCreationTime() const { 
     return impl->GetCurrentFindData().ftCreationTime; 
    }; 
    FILETIME GetLastAccessTime() const { 
     return impl->GetCurrentFindData().ftLastAccessTime; 
    }; 
    FILETIME GetLastWriteTime() const { 
     return impl->GetCurrentFindData().ftLastWriteTime; 
    }; 
}; 

struct AllResults 
{ 
    template <typename Recurse_T> 
    bool operator()(const FileData<Recurse_T>&) { 
     return true; 
    }; 
}; 

struct FilesOnly 
{ 
    template <typename Recurse_T> 
    bool operator()(const FileData<Recurse_T>& arg) { 
     return arg.IsFile(); 
    }; 
}; 

#pragma warning(push) 
#pragma warning(disable: 4355) 
template <typename Filter_T, typename Recurse_T> 
class DirectoryIterator : public boost::iterator_facade<DirectoryIterator<Filter_T>, const FileData<Recurse_T>, std::input_iterator_tag> 
{ 
    friend class boost::iterator_core_access; 
    boost::shared_ptr<Recurse_T> impl; 
    FileData<Recurse_T> derefData; 
    Filter_T filter; 
    void increment() { 
     do { 
      impl->increment(); 
     } while (! filter(derefData)); 
    }; 
    bool equal(const DirectoryIterator& other) const { 
     return impl->equal(*other.impl); 
    }; 
    const FileData<Recurse_T>& dereference() const { 
     return derefData; 
    }; 
public: 
    typedef FileData<Recurse_T> DataType; 
    friend struct DataType; 
    DirectoryIterator(Filter_T functor = Filter_T()) : 
     impl(boost::make_shared<Recurse_T>()), 
     derefData(this), 
     filter(functor) { 
    }; 
    explicit DirectoryIterator(const std::wstring& pathSpec, Filter_T functor = Filter_T()) : 
     impl(boost::make_shared<Recurse_T>(pathSpec)), 
     derefData(this), 
     filter(functor) { 
    }; 
}; 
#pragma warning(pop) 

}} 
+4

如果只是標題,我認爲你最好的選擇是把它分成多個文件。像「detail/DirectoryIterator.hpp」等,至少它不包含在一個文件中。這就是Boost所做的。順便說一句,Boost有一個文件系統庫,以防萬一你不知道。 – GManNickG 2010-04-07 19:22:01

+0

@GMan:+1。是的,boost有一個文件系統庫,但它是爲跨平臺兼容性設計的,它需要打開我不想要的一整套蠕蟲(例如它有自己的路徑處理器)。有了這個,我想我可以讓客戶端代碼更清晰,不關心跨平臺可移植性。 – 2010-04-07 19:25:24

+0

很高興看到你越來越好用了:http://stackoverflow.com/questions/2531874/how-might-i-wrap-the-findxfile-style-apis-to-the-stl-style-iterator -pattern-in-c – 2010-04-07 19:30:37

回答

9

我有我的一些更多的代碼,如果這是任何安慰的。所有C++標準庫實現,Boost和Microsoft(例如ATL)都是如此。

1

只要頭文件的長度變長,您可以在頭文件中使用盡可能多的代碼。權衡是每次構建程序時必須重新編譯的代碼量;可以將放置在CPP文件中的代碼編譯爲對象文件並在每個後續構建中鏈接。

我會建議DirectoryIteratorImpl的每個方法定義應該被移動到.cpp文件。如果你沒有在類定義中內聯定義一個方法,那麼沒有理由將它包含在頭文件中。

無關聯:避免編寫inline DirectoryIteratorImpl(); - 實際上將內聯函數內聯編寫,或者不要將它們標記爲內聯。從C++ FAQ Lite

它通常是至關重要的是,函數定義 被放置在頭文件(之間的{...}的部分)。如果將內聯函數的定義放入.cpp 文件中,並且它是從其他.cpp文件調用的,則會從鏈接程序中收到「無法解析的外部」 錯誤。

如果你的函數在頭文件中寫的太「大」,它們太大而不能嵌入,編譯器可能會忽略你的內聯建議。

+0

#1。你沒有回答我的問題。我知道代碼會被放入使用此代碼的每個文件中 - 我想知道是否會導致代碼膨脹太多。 #2。我不能讓這些函數成爲類的文字部分,因爲其中一半取決於'class FileData'的完整定義。你爲什麼認爲我隱式地聲明瞭所有'FileData'的成員? :) – 2010-04-07 19:33:41

+0

@billy也許問題的主體應該已經指定。另外,如果你不能內聯聲明函數,我的建議就是--in'inline'定義。 – meagar 2010-04-07 19:37:54

+0

@meagar:我認爲這是暗示的。內聯函數的含義應該是固有的。我問是否這是一個好主意。如果它導致了過多的代碼膨脹,那麼它會成爲一個壞主意的唯一原因是。 – 2010-04-07 19:46:55

6

唯一引起我對開放性問題的部分是DirectoryIteratorImpl中函數的實現。它不是一個模板,所以它不一定要放在頭文件中,它有一些比較長的例程(「真正的」構造函數和增量)。

其餘的要麼是模板,要麼是由你希望它們在任何情況下內聯的瑣碎函數組成(例如,FileData的成員)。無論如何,這些將會以頭部結尾。

+0

達到每日投票限額; 4小時後回來。 < - 該死!我會在4個小時內註冊。 – 2010-04-07 19:41:57

+0

@比利:哈,你肯定會用完很多票。爲你+1。 – GManNickG 2010-04-07 20:08:48

+0

@GMan:今天早上8點纔出發。 *比爾跑到元申請更多的選票。 (開玩笑) – 2010-04-07 20:29:52

1

看來你在這裏爲Windows編程,我們是否應該假設你正在使用Visual Studio?

無論如何,我不認爲在標題中有太多的代碼。

它的權衡主要的問題:

  • 慢編譯(但我們有多核和預編譯頭)
  • 更頻繁的重新編譯(再次,多核)
  • 也許代碼膨脹...

令人討厭的(在我看來)唯一的一點是最新的......我在這裏需要幫助:我們確定函數將被內聯,是不是可能編譯r和鏈接器決定不將它們內聯並將它們轉換爲常規調用?

坦率地說,我不會擔心太多。一些Boost庫僅僅是頭文件,即使它們的非模板部分只是因爲它使集成更容易(不需要鏈接)。