Frage

Es scheint, wie ich hier ziemlich viel Code Inline hatte. Ich frage mich, ob es schlechtes Design der Praxis in einer Header-Datei wie diese ganz zu verlassen:

#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)

}}
War es hilfreich?

Lösung

Ich habe viel mehr Code in ein paar von mir, wenn es das ist jeder Trost. und so tun, alle C ++ Standard Library Implementierungen Boost und Microsoft (zum Beispiel ATL).

Andere Tipps

Der einzige Teil, der mich schlägt als zu viel Frage offen wäre Implementierungen der Funktionen in DirectoryIteratorImpl sein. Es ist nicht eine Schablone, also sie nicht wirklich haben in einem Header sein, und es hat ein paar etwas länger Routinen (den „echten“ Konstruktor und das Schrittweite).

Der Rest sind entweder Vorlagen oder auch solche trivialen Funktionen zusammengesetzt Sie sie inline in jedem Fall wollen würde (zum Beispiel die Mitglieder der FileData). Diese werden in einem Header in jedem Fall am Ende.

Was die Länge des Headers geht, können Sie so viel Code haben, wie Sie in Ihrem Header-Dateien möchten. Der Trade-off ist die Menge an Code, der jedes Mal, wenn Ihr Programm gebaut neu kompiliert werden müssen; Code in Ihren CPP-Dateien platziert kann in Objektdateien und verknüpfen in auf jedem nachfolgenden Build kompiliert werden.

Ich würde vorschlagen, dass jeder der Methodendefinitionen für DirectoryIteratorImpl sollte auf eine .cpp Datei verschoben werden. Wenn Sie nicht eine Methode Inline innerhalb einer Klassendefinition definieren, gibt es keinen Grund dafür in der Header-Datei aufgenommen werden.

Eine unabhängige beiseite: Vermeiden Sie schriftlich inline DirectoryIteratorImpl(); - eigentlich schreiben Sie Ihre Inline-Funktionen inline, oder sie nicht inline markieren. Von der C ++ FAQ Lite :

  

Es ist in der Regel zwingend notwendig, dass die Funktion der Definition (der Teil zwischen der {...})   wird in einer Header-Datei platziert. Wenn Sie eine CPP setzen die Definition der Inline-Funktion in   Datei, und wenn es von einem anderen CPP-Datei aufgerufen wird, erhalten Sie eine „nicht aufgelöstes externes“ bekommen   Fehler aus dem Linker.

Wenn Sie Ihre Funktionen „zu groß“ zu schreiben, in der Header-Datei sind, sind sie zu groß, um zu Inline- und die Compiler Ihren Inline-Vorschlag wahrscheinlich ignorieren sowieso.

Es scheint, dass Sie für Windows programmieren hier, gehen wir davon aus, dass Sie Visual Studio?

verwenden

Wie auch immer, ich glaube nicht, dass es etwas wie zu viel Code in Header ist.

Es ist eine Frage von Kompromissen meist:

  • langsame Kompilierung (aber wir haben Multicores und vorkompilierte Header)
  • häufigere erneute Kompilierung (wieder, Multicores)
  • vielleicht Code aufblasen ...

Der einzige Punkt, der ärgerlich ist (meiner Meinung nach) ist die neueste ... und ich werde Hilfe braucht hier: sind wir sicher, dass die Funktionen inlined zu gehen, ist es nicht möglich, dass der Compiler und Linker entscheiden sie nicht Inline- und sie in einen regulären Anruf umwandeln?

Ehrlich gesagt, ich würde nicht zu viele Sorgen zu machen. Eine Reihe von Boost Bibliotheken sind Kopf nur noch für ihre Nicht-Template-Teile einfach, weil es die Integration erleichtert (nicht erforderlich Linking).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top