Является это слишком много кода для заголовка только библиотекой?

Кажется, что я должен был встроить довольно мало кода здесь. Я задаюсь вопросом - ли это плохая практика дизайна для отъезда этого полностью в заголовочном файле как это:

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

}}
5
задан Billy ONeal 7 April 2010 в 21:18
поделиться

4 ответа

У меня гораздо больше кода в некоторых моего, если это утешит. как и все реализации стандартной библиотеки C ++, Boost и Microsoft (например, ATL).

9
ответ дан 18 December 2019 в 09:48
поделиться

Единственная часть, которая вызывает у меня много вопросов, - это реализация функций из DirectoryIteratorImpl . Это не шаблон, поэтому он не обязательно должен быть в заголовке, и у него есть несколько более длинных подпрограмм («настоящий» конструктор и приращение).

Остальные являются либо шаблонами, либо состоят из таких тривиальных функций, которые вы в любом случае захотите встроить (например, члены FileData ). В любом случае они попадут в заголовок.

6
ответ дан 18 December 2019 в 09:48
поделиться

Что касается длины заголовка, вы можете иметь столько кода, сколько захотите, в ваших файлах заголовков. Компромисс - это объем кода, который необходимо перекомпилировать каждый раз при построении вашей программы; код, размещенный в ваших файлах CPP, можно скомпилировать в объектные файлы и связать с ними при каждой последующей сборке.

Я бы посоветовал переместить каждое из определений методов для DirectoryIteratorImpl в файл .cpp . Если вы не определяете метод, встроенный в определение класса, нет причин для его включения в файл заголовка.

Несвязанное замечание: избегайте записи inline DirectoryIteratorImpl (); - фактически пишите встроенные функции встроенными или не помечайте их встроенными. Из C ++ FAQ Lite :

Обычно обязательно, чтобы определение функции (часть между {...}) было помещено в файл заголовка. Если вы поместите определение встроенной функции в файл .cpp и если она будет вызвана из другого файла .cpp, вы получите от компоновщика ошибку "неразрешенная внешняя" .

Если ваши функции «слишком велики» для записи в файл заголовка, они слишком велики для встраивания, и компилятор, скорее всего, все равно проигнорирует ваше встроенное предложение.

1
ответ дан 18 December 2019 в 09:48
поделиться

Кажется, вы здесь программируете для Windows. Можно ли предположить, что вы используете Visual Studio?

В любом случае, я не думаю, что здесь что-то есть как слишком много кода в заголовках.

В основном это вопрос компромиссов:

  • более медленная компиляция (но у нас есть многоядерные и предварительно скомпилированные заголовки)
  • более частая перекомпиляция (опять же, многоядерные)
  • возможно раздувание кода ...

Единственное раздражающая (на мой взгляд) точка является последней ... и здесь мне понадобится помощь: уверены ли мы, что функции будут встроены, не возможно ли, чтобы компилятор и компоновщик решили не встраивать их и превратить их в обычный звонок?

Честно говоря, я бы не стал особо беспокоиться об этом. Некоторые библиотеки Boost предназначены только для заголовков даже для их частей, не являющихся шаблонами, просто потому, что это упрощает интеграцию (связывание не требуется).

1
ответ дан 18 December 2019 в 09:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: