2017-05-04 57 views
0

여러 파일을 동시에 차단 한 다음 다른 위치로 복사하려고 시도했습니다.QFutureWatcher에서 QFuture 결과를 지우는 방법

원본 파일과 대상 파일을 동시에 차단해야합니다. 따라서 static QFile::copy() 기능을 사용할 수 없습니다.

QFile으로 인해 파일을 보관하고 이동하려면 QSharedPointer<QFile>을 사용할 수 없습니다. 복사 및 이동이 불가능합니다.

전체 작업을 완전히 수행하려면 QtConcurrent 프레임 워크를 사용하십시오. 즉 : QtConcurrent::mappedReducedQFutureWatcher.

내가 펑터를 줄이고 사용에 복사 한 후 순차적으로, 지도 펑를 사용하는 파일의 모든 쌍을 엽니 다.

using PFile = QSharedPointer<QFile>; 

using PFileList = QList<PFile>; 

template< typename Result, typename Functor > 
struct FunctorWithResultType 
     : std::decay_t<Functor> 
{ 

    using result_type = Result; 

    FunctorWithResultType(Functor & functor) 
     : std::decay_t<Functor>{std::forward<Functor>(functor)} 
    { ; } 

}; 

template< typename Result, typename Functor > 
FunctorWithResultType< Result, Functor > 
addResultType(Functor && functor) 
{ 
    return {functor}; 
} 

class Test 
{ 
public : 

    QDir currentDirectory; 
    QFutureWatcher<PFileList> fileCopyFutureWatcher; 
    // ... 
}; 

// ... 

Test::Test() 
{ 
    auto onFilesCopied = [&] 
    { 
     PFileList copiedFiles = fileCopyFutureWatcher.result(); 
     qCInfo(usbDevice) << "Files copy operation from drive finished."; 
     qCInfo(usbDevice) << copiedFiles.size(); 
     for (PFile const & file : copiedFiles) { 
      //file->close(); // I want copiedFiles to be closed automatically at the end of current scope 
     } 
    }; 
    connect(&fileCopyFutureWatcher, &fileCopyFutureWatcher.finished, onFilesCopied); 
} 

// ... 

void Test::onDeviceAdded(QString deviceName) 
{ 
    qCInfo(usbDevice) << "USB device" << deviceName << "is added."; 
    if (!fileCopyFutureWatcher.isFinished()) { 
     return; 
    } 
    QDir sourceDirectory{deviceName}; 
    if (!sourceDirectory.cd("dir")) { 
     qCInfo(usbDevice) << "Drive" << deviceName << "does not contain dir subdirectory."; 
     return; 
    } 
    auto destinationDirectory = currentDirectory; 
    if (!destinationDirectory.mkpath("fileCache")) { 
     qCCritical(usbDevice) << "Can't create fileCache subdirectory in" 
            << destinationDirectory.absolutePath() 
            << "directory"; 
     return; 
    } 
    if (!destinationDirectory.cd("fileCache")) { 
     qCCritical(usbDevice) << "Can't change directory to fileCache subdirectory in" 
            << destinationDirectory.absolutePath() 
            << "directory"; 
     return; 
    } 
    struct PFilePair 
    { 
     PFile source, destination; 
    }; 
    auto openSourceAndDestinationFiles = [&, destinationDirectory] (QFileInfo const & fileInfo) -> PFilePair 
    { 
     auto source = PFile::create(fileInfo.absoluteFilePath()); 
     QFile & sourceFile = *source; 
     if (!sourceFile.open(QFile::ReadOnly)) { 
      qCCritical(usbDevice) << "Can't open file" << sourceFile.fileName() 
             << "to read:" << sourceFile.errorString(); 
      return {}; 
     } 
     auto destination = PFile::create(destinationDirectory.absoluteFilePath(fileInfo.fileName())); 
     QFile & destinationFile = *destination;; 
     if (!destinationFile.open(QFile::ReadWrite | QFile::Truncate)) { 
      qCCritical(usbDevice) << "Can't open file" << destinationFile.fileName() 
             << "to write:" << destinationFile.errorString(); 
      return {}; 
     } 
     return {qMove(source), qMove(destination)}; 
    }; 
    auto copyFiles = [&] (PFileList & files, PFilePair const & filePair) 
    { 
     if (filePair.source.isNull() || filePair.destination.isNull()) { 
      return; 
     } 
     QFile & sourceFile = *filePair.source; 
     QFile & destinationFile = *filePair.destination; 
     qCInfo(usbDevice) << sourceFile.fileName() << "->" << destinationFile.fileName(); 
     constexpr int size = (1 << 20); // 1MiB 
     QByteArray buffer{size, 0}; 
     char * const data = buffer.data(); 
     while (!sourceFile.atEnd()) { 
      auto bytesRead = sourceFile.read(data, size); 
      if (bytesRead < 0) { 
       qCCritical(usbDevice) << "Can't read file" << sourceFile.fileName() 
              << ":" << sourceFile.errorString(); 
       return; 
      } 
      auto bytesWritten = destinationFile.write(data, bytesRead); 
      while (bytesWritten < bytesRead) { 
       auto sizeWritten = destinationFile.write(data + bytesWritten, bytesRead - bytesWritten); 
       if (sizeWritten < 0) { 
        qCCritical(usbDevice) << "Can't write file" << destinationFile.fileName() 
               << ":" << destinationFile.errorString(); 
        return; 
       } 
       bytesWritten += sizeWritten; 
      } 
      Q_ASSERT(bytesWritten == bytesRead); 
     } 
     Q_ASSERT(sourceFile.size() == destinationFile.size()); 
     destinationFile.flush(); 
     files.append(filePair.destination); 
    }; 
    QStringList nameFilters; 
    nameFilters << "file.dat"; 
    // many other entries 
    auto entryInfoList = sourceDirectory.entryInfoList(nameFilters, (QDir::Readable | QDir::Files)); 
    fileCopyFutureWatcher.setFuture(QtConcurrent::mappedReduced<PFileList>(qMove(entryInfoList), addResultType<PFilePair>(openSourceAndDestinationFiles), copyFiles)); 
} 

// ... 

QFutureWatcher::finished 이벤트의 결과를 읽은 후 모든 대상 파일은 여전히 ​​열리고 내 응용 프로그램에 의해 차단. 그러므로 나는 QSharedPointer<QFile>의 사본이 여전히 존재한다고 결론을 내릴 수 있습니다. 나는 그들이 QFutureWatcher 안에 QFuture 안에 떠나는 것을 의심한다. 가짜 QFuture 인스턴스로 QFutureWatcher::setFuture을 호출하지 않고도 모든 파일을 지우려면 어떻게해야합니까 (즉, QFile::~QFile()에서 모든 파일을 닫는 방법)?

이 결과를 복사하려면 QFuture의 결과를 도용해야합니다.

답변

0

내 해결 방법은 다음과 같습니다 :

using PFile = QSharedPointer<QFile>; 

using PFileList = QList<PFile>; 

template< typename Result, typename Functor > 
struct FunctorWithResultType 
     : std::decay_t<Functor> 
{ 

    using result_type = Result; 

    FunctorWithResultType(Functor & functor) 
     : std::decay_t<Functor>{std::forward<Functor>(functor)} 
    { ; } 

}; 

template< typename Result, typename Functor > 
FunctorWithResultType< Result, Functor > 
addResultType(Functor && functor) 
{ 
    return {functor}; 
} 

class Test 
{ 
public : 

    QDir currentDirectory; 
    // ... 
}; 

// ... 

void Test::onDeviceAdded(QString deviceName) 
{ 
    qCInfo(usbDevice) << "USB device" << deviceName << "is added."; 
    QDir sourceDirectory{deviceName}; 
    if (!sourceDirectory.cd("dir")) { 
     qCInfo(usbDevice) << "Drive" << deviceName << "does not contain dir subdirectory."; 
     return; 
    } 
    auto destinationDirectory = currentDirectory; 
    if (!destinationDirectory.mkpath("fileCache")) { 
     qCCritical(usbDevice) << "Can't create fileCache subdirectory in" 
            << destinationDirectory.absolutePath() 
            << "directory"; 
     return; 
    } 
    if (!destinationDirectory.cd("fileCache")) { 
     qCCritical(usbDevice) << "Can't change directory to fileCache subdirectory in" 
            << destinationDirectory.absolutePath() 
            << "directory"; 
     return; 
    } 
    struct PFilePair 
    { 
     PFile source, destination; 
    }; 
    auto openSourceAndDestinationFiles = [&, destinationDirectory] (QFileInfo const & fileInfo) -> PFilePair 
    { 
     auto source = PFile::create(fileInfo.absoluteFilePath()); 
     QFile & sourceFile = *source; 
     if (!sourceFile.open(QFile::ReadOnly)) { 
      qCCritical(usbDevice) << "Can't open file" << sourceFile.fileName() 
             << "to read:" << sourceFile.errorString(); 
      return {}; 
     } 
     auto destination = PFile::create(destinationDirectory.absoluteFilePath(fileInfo.fileName())); 
     QFile & destinationFile = *destination;; 
     if (!destinationFile.open(QFile::ReadWrite | QFile::Truncate)) { 
      qCCritical(usbDevice) << "Can't open file" << destinationFile.fileName() 
             << "to write:" << destinationFile.errorString(); 
      return {}; 
     } 
     return {qMove(source), qMove(destination)}; 
    }; 
    auto copyFiles = [&] (PFileList & files, PFilePair const & filePair) 
    { 
     if (filePair.source.isNull() || filePair.destination.isNull()) { 
      return; 
     } 
     QFile & sourceFile = *filePair.source; 
     QFile & destinationFile = *filePair.destination; 
     qCInfo(usbDevice) << sourceFile.fileName() << "->" << destinationFile.fileName(); 
     constexpr int size = (1 << 20); // 1MiB 
     QByteArray buffer{size, 0}; 
     char * const data = buffer.data(); 
     while (!sourceFile.atEnd()) { 
      auto bytesRead = sourceFile.read(data, size); 
      if (bytesRead < 0) { 
       qCCritical(usbDevice) << "Can't read file" << sourceFile.fileName() 
              << ":" << sourceFile.errorString(); 
       return; 
      } 
      auto bytesWritten = destinationFile.write(data, bytesRead); 
      while (bytesWritten < bytesRead) { 
       auto sizeWritten = destinationFile.write(data + bytesWritten, bytesRead - bytesWritten); 
       if (sizeWritten < 0) { 
        qCCritical(usbDevice) << "Can't write file" << destinationFile.fileName() 
               << ":" << destinationFile.errorString(); 
        return; 
       } 
       bytesWritten += sizeWritten; 
      } 
      Q_ASSERT(bytesWritten == bytesRead); 
     } 
     Q_ASSERT(sourceFile.size() == destinationFile.size()); 
     destinationFile.flush(); 
     files.append(filePair.destination); 
    }; 

    auto & fileCopyFutureWatcher = *new QFutureWatcher<PFileList>{this}; 

    auto onFilesCopied = [&] 
    { 
     fileCopyFutureWatcher.deleteLater(); 
     PFileList copiedFiles = fileCopyFutureWatcher.result(); 
     qCInfo(usbDevice) << "Files copy operation from drive finished."; 
     qCInfo(usbDevice) << copiedFiles.size(); 
     for (PFile const & file : copiedFiles) { 
      // ... 
     } 
    }; 
    connect(&fileCopyFutureWatcher, &fileCopyFutureWatcher.finished, onFilesCopied); 

    QStringList nameFilters; 
    nameFilters << "*.dat"; 
    auto entryInfoList = sourceDirectory.entryInfoList(nameFilters, (QDir::Readable | QDir::Files)); 
    fileCopyFutureWatcher.setFuture(QtConcurrent::mappedReduced<PFileList>(qMove(entryInfoList), addResultType<PFilePair>(openSourceAndDestinationFiles), copyFiles)); 
} 

// ... 

나는 거의 확실, 그 fileCopyFutureWatcher 인스턴스 delete 안정적 d를 할 것이다.