هل هذا الكثير من التعليمات البرمجية لمكتبة الرأس فقط؟

StackOverflow https://stackoverflow.com/questions/2595204

  •  25-09-2019
  •  | 
  •  

سؤال

يبدو أنه كان عليّ أن أضمن الكثير من التعليمات البرمجية هنا. أتساءل عما إذا كان من الممارسة التصميم السيئة أن تترك هذا بالكامل في ملف رأس مثل هذا:

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

}}
هل كانت مفيدة؟

المحلول

لدي المزيد من الكود في بعض من الألغام ، إذا كان ذلك من أي عزاء. وكذلك جميع تطبيقات المكتبة القياسية C ++ ، Boost و Microsoft (على سبيل المثال ، ATL).

نصائح أخرى

الجزء الوحيد الذي يذهلني على أنه منفتح على الكثير من الأسئلة هو تطبيقات الوظائف في DirectoryIteratorImpl. إنه ليس قالبًا ، لذا لا يجب أن يكون في رأسه ، ولديه روتين أطول إلى حد ما (المُنشئ "الحقيقي" والزيادة).

الباقي إما قوالب أو مكونة من هذه الوظائف التافهة التي تريدها في أي حال (على سبيل المثال ، أعضاء FileData). سوف ينتهي بهم المطاف في رأس في أي حال.

بقدر ما يذهب طول الرأس ، يمكنك الحصول على أكبر عدد من التعليمات البرمجية كما تريد في ملفات الرأس الخاصة بك. تداول التداول هو مقدار الكود الذي يجب إعادة تنسيقه في كل مرة يتم فيها بناء البرنامج ؛ يمكن تجميع التعليمات البرمجية الموضوعة في ملفات CPP في ملفات الكائنات وربطها في كل بناء لاحق.

أود أن أقترح أن كل من تعريفات الطريقة DirectoryIteratorImpl يجب نقلها إلى .cpp ملف. إذا كنت لا تحدد طريقة مضمنة داخل تعريف الفصل ، فلا يوجد سبب لإدراجه في ملف الرأس.

جانبا لا علاقة لها: تجنب الكتابة inline DirectoryIteratorImpl(); - في الواقع اكتب وظائفك المضمنة مضمّنة ، أو لا تضع علامة عليها في الخط. من C ++ الأسئلة الشائعة لايت:

عادة ما يكون من الضروري وضع تعريف الوظيفة (الجزء بين {...}) في ملف رأس. إذا وضعت تعريف الوظيفة المضمنة في ملف .cpp ، وإذا تم استدعاؤه من بعض ملف .cpp آخر ، فستحصل على خطأ "خارجي لم يتم حله" من الرابط.

إذا كانت وظائفك "كبيرة جدًا" التي يجب الكتابة عليها في ملف الرأس ، فهي كبيرة جدًا من عدم وجودها ، ومن المحتمل أن يتجاهل المترجم اقتراحك المضمن على أي حال.

يبدو أنك تقوم برمجة لنظام التشغيل Windows هنا ، هل نفترض أنك تستخدم Visual Studio؟

على أي حال ، لا أعتقد أن هناك شيء كود كبير في الرؤوس.

إنها مسألة تداول في الغالب:

  • مجموعة أبطأ (لكن لدينا رؤوس متعددة ورؤوس مسبقة)
  • إعادة التكرار أكثر تواترا (مرة أخرى ، متعددة)
  • ربما رمز الانتفاخ ...

النقطة الوحيدة المزعجة (في رأيي) هي الأحدث ... وسأحتاج إلى مساعدة هنا: هل نحن متأكدون من أن الوظائف ستكون مضطربة ، أليس من الممكن أن يقرر المترجم والرابط عدم المضمون لهم وتحويلهم إلى مكالمة منتظمة؟

بصراحة ، لن أقلق كثيرًا بشأن ذلك. عدد من Boost المكتبات هي رأس فقط حتى بالنسبة لأجزائها غير المبتدئين لمجرد أنها تجعل التكامل أسهل (لا يوجد ارتباط مطلوب).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top