Pregunta

Parece como si tuviera que inline un poco de código aquí. Me pregunto si se trata de una mala práctica de diseño para salir de esta enteramente en un fichero de cabecera de esta manera:

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

}}
¿Fue útil?

Solución

Tengo mucho más código en algunas de las minas, si eso es de consuelo. y también lo hacen todas las implementaciones de C ++ biblioteca estándar, Boost y Microsoft (por ejemplo, ATL).

Otros consejos

La única parte que me parece estar abierto a gran pregunta sería implementaciones de las funciones en DirectoryIteratorImpl. No es una plantilla de modo que en realidad no tiene por qué ser en un encabezado, y tiene un par de rutinas algo más largos (el constructor "real" y el incremento).

El resto son o bien las plantillas o también compuestos de tales funciones triviales que te gustaría ellos en línea, en cualquier caso (por ejemplo, los miembros de FileData). Aquellos va a terminar en un encabezado en cualquier caso.

En cuanto a la longitud de la cabecera se va, usted puede tener tanto el código como desee en sus archivos de cabecera. El descuento comercial es la cantidad de código que se debe volver a compilar cada vez que el programa está construido; código que se coloca en los archivos de CPP se puede compilar en archivos objeto y ligado en el cada generación subsiguiente.

Yo sugeriría que cada una de las definiciones de métodos para DirectoryIteratorImpl debe ser trasladado a un archivo .cpp. Si usted no está definiendo una línea método dentro de una definición de clase, no hay razón para que pueda ser incluido en el archivo de cabecera.

Una relación de lado: evitar escribir inline DirectoryIteratorImpl(); - en realidad escribir sus funciones en línea en línea, o no los marca en línea. Desde el C ++ FAQ Lite :

  

Por lo general es imperativo que la definición de la función (la parte entre el {...})   ser colocado en un archivo de cabecera. Si pones la definición de la función en línea en un .cpp   archivo y si se llama desde algún otro archivo .cpp, obtendrá un "externo no resuelto"   de error del enlazador.

Si sus funciones son "demasiado grande" para escribir en el archivo de cabecera, son demasiado grandes para el compilador en línea y es probable que haga caso de su sugerencia en línea de todos modos.

Parece que se está programando para Windows aquí, vamos a suponer que usted está utilizando Visual Studio?

De todos modos, no creo que hay algo como demasiado código en las cabeceras.

Es una cuestión de compensaciones en su mayoría:

  • compilación más lenta (pero tenemos multicores y encabezados precompilados)
  • recompilación más frecuente (de nuevo, multicores)
  • quizá código hincha ...

El único punto que es molesto (en mi opinión) es la última ... y necesitaré ayuda aquí: ¿estamos seguros de las funciones que vamos a ser inline, ¿no es posible que el compilador y enlazador deciden no inline ellos y transformarlos en una llamada normal?

Francamente, yo no preocuparse demasiado acerca de eso. Un número de bibliotecas Boost son sólo de encabezado incluso para sus piezas no simplemente plantilla, ya que hace más fácil la integración (sin enlazado requerida).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top