Domanda

Sembra che ho dovuto inline un po 'di codice qui. Mi chiedo se si tratta di una cattiva pratica di progettazione per uscire da questo interamente in un file di intestazione in questo modo:

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

}}
È stato utile?

Soluzione

Molte cose ho ancora il codice in alcuni dei miei, se questo è di qualche consolazione. e così fare tutto C ++ Standard Library implementazioni, Boost e Microsoft (per esempio, ATL).

Altri suggerimenti

L'unica parte che mi sembra essere aperto a più domanda sarebbe implementazioni delle funzioni in DirectoryIteratorImpl. Non è un modello in modo che non hanno davvero di essere in un colpo di testa, e ha un paio di routine un po 'più lunghi (il costruttore "reale" e l'incremento).

Il resto sono o modelli o altro composti di tali funzioni banali che ci si vuole loro linea, in ogni caso (ad esempio, i membri di FileData). Coloro che finirà in un colpo di testa in ogni caso.

Per quanto riguarda la lunghezza dell'intestazione va, si può avere tanto codice come vuoi nei file di intestazione. Il fuori commercio è la quantità di codice che deve essere ricompilato ogni volta che il programma è costruito; codice inserito nei file CPP può essere compilato in file oggetto e collegato in su ogni generazione successiva.

I suggerirebbe che ciascuna delle definizioni dei metodi per DirectoryIteratorImpl dovrebbe essere spostato in un file .cpp. Se non si sta definendo un metodo inline all'interno di una definizione di classe, non c'è motivo per essere incluso nel file di intestazione.

Un estraneo a parte: la scrittura inline DirectoryIteratorImpl(); Evitare - in realtà scrivere le funzioni inline, o non contrassegnarli in linea. Dal C ++ FAQ Lite :

  

Di solito è indispensabile che la definizione della funzione (la parte tra il {...})   essere collocato in un file di intestazione. Se si mette la definizione della funzione inline in una cpp   File e se si chiama da un altro file cpp, si otterrà un "esterno non risolto"   errore dal linker.

Se le funzioni sono "troppo grandi" per scrivere nel file di intestazione, sono troppo grandi per inline e il compilatore probabilmente ignorare il suo suggerimento in linea in ogni modo.

Sembra che si sta programmando per Windows qui, dobbiamo supporre che si sta utilizzando Visual Studio?

In ogni caso, non credo che ci sia qualcosa di troppo codice nelle intestazioni.

E 'una questione di compromessi per lo più:

  • compilation più lento (ma abbiamo multicore e le intestazioni precompilate)
  • ricompilazione più frequente (di nuovo, multicore)
  • forse pesantezza del codice ...

L'unico punto che è fastidioso (a mio parere) è l'ultimo ... e avrò bisogno di aiuto qui: siamo sicuri le funzioni accingiamo ad essere inline, non è possibile che il compilatore e linker decidere non inline e trasformarli in una chiamata normale?

Francamente, non mi preoccuperei troppo di questo. Un certo numero di librerie Boost sono header-, anche se solo per le loro parti non-modello semplicemente perché rende più facile l'integrazione (senza linking richiesto).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top