การใช้รูปแบบพร็อกซีกับตัววนซ้ำ C++

ฉันมีตัววนซ้ำที่ซับซ้อนปานกลางซึ่งเขียนล้อม FindXFile apis บน Win32 (ดู คำถามก่อนหน้า) เพื่อหลีกเลี่ยงค่าใช้จ่ายในการสร้างวัตถุที่ทำซ้ำการทำงานของโครงสร้าง WIN32_FIND_DATAW เป็นหลัก ฉันมีวัตถุพร็อกซีซึ่งทำหน้าที่เป็นประเภทการอ้างอิง const ไปยัง WIN32_FIND_DATAW เดียวซึ่งได้รับการประกาศ ภายในอวัยวะภายในที่ไม่สามารถคัดลอกได้ของตัววนซ้ำ เรื่องนี้เยี่ยมมากเพราะว่า

  1. ลูกค้าไม่ต้องจ่ายเงินสำหรับการสร้างข้อมูลที่ไม่เกี่ยวข้องที่พวกเขาอาจจะไม่ได้ใช้ (ส่วนใหญ่ผู้คนจะสนใจเพียงชื่อไฟล์เท่านั้น) และ
  2. ลูกค้าสามารถรับข้อมูลทั้งหมดที่ได้รับจาก FindXFile API หากพวกเขาต้องการหรือต้องการข้อมูลนี้

สิ่งนี้จะกลายเป็นปัญหาเนื่องจากมีสำเนาข้อมูลจริงของออบเจ็กต์เพียงชุดเดียวเท่านั้น ดังนั้น เมื่อตัววนซ้ำถูกเพิ่ม พรอกซีทั้งหมดจะใช้งานไม่ได้ (ตั้งค่าเป็นไฟล์ถัดไปที่ตัววนซ้ำชี้ไป)

ฉันกังวลว่านี่เป็นปัญหาสำคัญหรือไม่ เพราะฉันสามารถนึกถึงกรณีที่วัตถุพร็อกซีไม่ทำงานตามที่ใครก็ตามคาดหวัง:

std::vector<MyIterator::value_type> files;
std::copy(MyIterator("Hello"), MyIterator(), std::back_inserter(files));

เนื่องจากเวกเตอร์ไม่มีอะไรนอกจากพรอกซีที่ไม่ถูกต้องจำนวนมาก ณ จุดนั้น ลูกค้าจำเป็นต้องดำเนินการบางอย่างแทน:

std::vector<std::wstring> filesToSearch;
std::transform(
 DirectoryIterator<FilesOnly>(L"C:\\Windows\\*"),
 DirectoryIterator<FilesOnly>(),
 std::back_inserter(filesToSearch),
 std::mem_fun_ref(&DirectoryIterator<FilesOnly>::value_type::GetFullFileName)
);

เมื่อเห็นสิ่งนี้ ฉันเข้าใจได้ว่าทำไมบางคนถึงไม่ชอบสิ่งที่ผู้ออกแบบไลบรารีมาตรฐานทำกับ std::vector<bool> ฉันยังคงสงสัยว่า: นี่เป็นการแลกเปลี่ยนที่สมเหตุสมผลเพื่อให้บรรลุ (1) และ (2) ข้างต้นหรือไม่ ถ้าไม่ มีวิธีใดที่จะยังบรรลุ (1) และ (2) โดยไม่ต้องใช้พรอกซี?

แก้ไข: อัปเดตรหัส:

#pragma once
#include <list>
#include <string>
#include <boost/noncopyable.hpp>
#include <boost/make_shared.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include "../Exception.hpp"

namespace WindowsAPI { namespace FileSystem {

class Win32FindData {
    WIN32_FIND_DATA internalData;
    std::wstring rootPath;
public:
    Win32FindData(const std::wstring& root, const WIN32_FIND_DATA& data) :
        rootPath(root), internalData(data) {};
    DWORD GetAttributes() const {
        return internalData.dwFileAttributes;
    };
    bool IsDirectory() const {
        return (internalData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
    };
    bool IsFile() const {
        return !IsDirectory();
    };
    unsigned __int64 GetSize() const {
        ULARGE_INTEGER intValue;
        intValue.LowPart = internalData.nFileSizeLow;
        intValue.HighPart = internalData.nFileSizeHigh;
        return intValue.QuadPart;
    };
    std::wstring GetFolderPath() const {
        return rootPath;
    };
    std::wstring GetFileName() const {
        return internalData.cFileName;
    };
    std::wstring GetFullFileName() const {
        return rootPath + L"\\" + internalData.cFileName;
    };
    std::wstring GetShortFileName() const {
        return internalData.cAlternateFileName;
    };
    FILETIME GetCreationTime() const {
        return internalData.ftCreationTime;
    };
    FILETIME GetLastAccessTime() const {
        return internalData.ftLastAccessTime;
    };
    FILETIME GetLastWriteTime() const {
        return internalData.ftLastWriteTime;
    };
};

class EnumerationMethod : public boost::noncopyable {
protected:
    WIN32_FIND_DATAW currentData;
    HANDLE hFind;
    std::wstring currentDirectory;
    EnumerationMethod() : hFind(INVALID_HANDLE_VALUE) {};
    void IncrementCurrentDirectory() {
        if (hFind == INVALID_HANDLE_VALUE) return;
        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);
        }
    };
    virtual ~EnumerationMethod() {
        if (hFind != INVALID_HANDLE_VALUE)
            FindClose(hFind);
    };
public:
    bool equal(const EnumerationMethod& other) const {
        if (this == &other)
            return true;
        return hFind == other.hFind;
    };
    Win32FindData dereference() {
        return Win32FindData(currentDirectory, currentData);
    };
};

class NonRecursiveEnumeration : public EnumerationMethod
{
public:
    NonRecursiveEnumeration() {};
    NonRecursiveEnumeration(const std::wstring& pathSpec) {
        std::wstring::const_iterator lastSlash =
            std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base();
        if (lastSlash != pathSpec.begin())
            currentDirectory.assign(pathSpec.begin(), lastSlash-1);
        hFind = FindFirstFileW(pathSpec.c_str(), &currentData);
        if (hFind == INVALID_HANDLE_VALUE)
            WindowsApiException::ThrowFromLastError();
        while (hFind != INVALID_HANDLE_VALUE && (!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L".."))) {
            IncrementCurrentDirectory();
        }
    };
    void increment() {
        IncrementCurrentDirectory();
    };
};

class RecursiveEnumeration : public EnumerationMethod
{
    std::wstring fileSpec;
    std::list<std::wstring> futureDirectories;
    std::list<std::wstring>::iterator directoryInsertLocation;
    void ShiftToNextDirectory() {
        if (futureDirectories.empty()) {
            hFind = INVALID_HANDLE_VALUE;
            return;
        }
        //Get the next directory
        currentDirectory = futureDirectories.front();
        futureDirectories.pop_front();
        directoryInsertLocation = futureDirectories.begin();
        std::wstring pathSpec(currentDirectory);
        if (!pathSpec.empty())
            pathSpec.push_back(L'\\');
        pathSpec.append(fileSpec);

        hFind = FindFirstFileW(pathSpec.c_str(), &currentData);
        if (hFind == INVALID_HANDLE_VALUE)
            WindowsApiException::ThrowFromLastError();
        while (!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L"..")) {
            IncrementCurrentDirectory();
        }
    };
    void IncrementAndShift() {
        if (hFind != INVALID_HANDLE_VALUE && (currentData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
            directoryInsertLocation = futureDirectories.insert(directoryInsertLocation,
                currentDirectory + L"\\" + currentData.cFileName);
            directoryInsertLocation++;
        }
        IncrementCurrentDirectory();
        if (hFind == INVALID_HANDLE_VALUE)
            ShiftToNextDirectory();
    };
public:
    RecursiveEnumeration() {};
    RecursiveEnumeration(const std::wstring& pathSpec) {
        std::wstring::const_iterator lastSlash =
            std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base();
        if (lastSlash != pathSpec.begin()) {
            futureDirectories.push_back(std::wstring(pathSpec.begin(), lastSlash-1));
            fileSpec.assign(lastSlash, pathSpec.end());
        } else {
            futureDirectories.push_back(std::wstring());
            fileSpec = pathSpec;
        }
        ShiftToNextDirectory();
    };
    void increment() {
        do {
            IncrementAndShift();
        } while (!PathMatchSpecW(currentData.cFileName, fileSpec.c_str()));
    };
};

struct AllResults
{
    bool operator()(const Win32FindData&) {
        return true;
    };
}; 

struct FilesOnly
{
    bool operator()(const Win32FindData& arg) {
        return arg.IsFile();
    };
};

template <typename Filter_T = AllResults, typename Recurse_T = NonRecursiveEnumeration>
class DirectoryIterator : 
    public boost::iterator_facade<DirectoryIterator<Filter_T, Recurse_T>, Win32FindData, std::input_iterator_tag, Win32FindData>
{
    friend class boost::iterator_core_access;
    boost::shared_ptr<Recurse_T> impl;
    Filter_T filter;
    void increment() {
        do {
            impl->increment();
        } while (! filter(impl->dereference()));
    };
    bool equal(const DirectoryIterator& other) const {
        return impl->equal(*other.impl);
    };
    Win32FindData dereference() const {
        return impl->dereference();
    };
public:
    DirectoryIterator(Filter_T functor = Filter_T()) :
        impl(boost::make_shared<Recurse_T>()),
        filter(functor) {
    };
    explicit DirectoryIterator(const std::wstring& pathSpec, Filter_T functor = Filter_T()) :
        impl(boost::make_shared<Recurse_T>(pathSpec)),
        filter(functor) {
    };
};

}}

person Billy ONeal    schedule 08.04.2010    source แหล่งที่มา


คำตอบ (1)


ฉันไม่เข้าใจว่าทำไมคุณไม่ให้ตัววนซ้ำสร้างวัตถุที่มีข้อมูล WIN32_FIND_DATAW เมื่อมันถูกยกเลิกการอ้างอิง แทนที่จะอ้างถึงวัตถุพร็อกซีนี้ที่ได้รับการแก้ไขเมื่อตัววนซ้ำเพิ่มขึ้น

ตัววนซ้ำจะถูกทำเครื่องหมายในตัววนซ้ำอินพุต ให้ผู้ใช้ตัดสินใจว่าต้องการสำเนาของข้อมูลไฟล์ที่อ้างถึงหรือไม่ การยกเลิกการอ้างอิงตัววนซ้ำควรส่งคืนออบเจ็กต์ที่สามารถคัดลอกได้พร้อมอินเทอร์เฟซที่คล้ายกันไปยังคลาส FileData ของคุณที่มีข้อมูลสมาชิกที่เหมาะสมซึ่งในคลาสสามารถคัดลอกได้ตามความจำเป็นเพื่อรักษาเอกลักษณ์ของออบเจ็กต์ คลาสนี้ไม่ต้องการพารามิเตอร์เทมเพลตที่ระบุว่าการดำเนินการค้นหาควรดำเนินการอย่างไร เนื่องจากออบเจ็กต์หากคลาสนั้นไม่ได้มีส่วนเกี่ยวข้องกับการค้นหาจริงๆ ยกเว้นว่าการดำเนินการค้นหาสามารถสร้างได้ - อ็อบเจ็กต์นี้มีเพียงข้อมูลเกี่ยวกับ ไฟล์.

จากนั้นเมื่อตัววนซ้ำถูกยกเลิกการอ้างอิง ตัววนซ้ำจะสามารถส่งคืนอ็อบเจ็กต์ประเภทนั้นได้

สิ่งที่ตัววนซ้ำทำเมื่อมีการเพิ่มขึ้นไม่จำเป็นต้องมีอะไรเกี่ยวข้องกับวัตถุที่ส่งคืนเมื่อมีการยกเลิกการอ้างอิง (แม้ว่าข้อมูลบางส่วนจะดูแย่มากเช่นวัตถุที่ถูกส่งคืนและ/หรือใช้ในการดำเนินการเพิ่มขึ้น ).

ฉันอาจจะเปลี่ยนชื่อคลาส FileData<> ที่คุณต้องเป็น FileFindDataInternal<> เนื่องจากมันใช้งานจริง ๆ เป็นส่วนหนึ่งของการดำเนินการภายในของตัววนซ้ำเท่านั้น

นั่นจะทำให้ชื่อ FileData ว่างขึ้นเพื่อใช้สำหรับคลาสที่ล้อมรอบข้อมูลที่ผู้ใช้สนใจ และผู้ใช้ควรคัดลอกได้

person Michael Burr    schedule 08.04.2010
comment
เหตุใดคุณจึงไม่มีตัววนซ้ำสร้างออบเจ็กต์ที่มีข้อมูล WIN32_FIND_DATAW เมื่อมีการยกเลิกการอ้างอิง ‹-- เพราะแล้ว การอ้างอิงทุกครั้งจำเป็นต้องคัดลอกโครงสร้าง WIN32_FIND_DATA ขนาด 580 ไบต์ทั้งหมด รวมถึงสตริงเส้นทางรูท แต่ถ้าคุณคิดว่านั่นเป็นการแลกเปลี่ยนที่ยอมรับไม่ได้ ฉันจะปรับโครงสร้างพรอกซีใหม่ - person Billy ONeal; 09.04.2010
comment
@Michael Burr: แก้ไขการแก้ไขคำถามของฉัน นั่นคือสิ่งที่คุณหมายถึง? - person Billy ONeal; 09.04.2010