Przekazywanie metody przez parametr

0

Cześć,

mam taką sytuację

 
void MainWindow::addFilesToTable( EnumFileTable tableId, 
                                  std::function<void( const QFile& )> &functionOnFile )
{
    auto filePaths = QFileDialog::getOpenFileNames();

    for( const auto &filePath : filePaths )
    {
        QFile file( filePath );
        if( file.open( QIODevice::ReadOnly ) )
        {
            fileTables[tableId].addFile( filePath );
            functionOnFile( file );
            file.close();
        }
    }
}

void MainWindow::newFileOnPartition( const QFile &file )
{
    hideInfo.freeSpace += file.size();
    ui->labFreeSpace->setText( "Total free space: " + QString::number( hideInfo.freeSpace ) );
}

void MainWindow::on_addFilesOnPartitionButton_clicked()
{
    addFilesToTable( FILETABLE_FILES_ON_PARTITION, /* nie wiem jak przekazać funkcję newFileOnPartition() */ );
}

i tak jak napisałem wcześniej, nie bardzo wiem jak przekazać do metody addFilesToTable(), żeby na każdym pliku wywołała metodę newFileOnPartition().

Z góry dzięki za pomoc

4

Żeby wywołać funkcję składową musisz stworzyć obiekt funkcyjny, który w pierwszym parametrze przekaże wskaźnika na klasę, na rzecz której ma zostać wywołana ta funkcja składowa. Żeby to zrobić można np. użyć std::bind. Tutaj masz przykład:

#include <iostream>
#include <functional>

class X {
public:
  void test(int v) {
  	caller(std::bind(&X::callbackMember, this, std::placeholders::_1), v);
  }
  
  void caller(std::function<void (int)> callback, int v) {
  	callback(v);
  }
  
  void callbackMember(int v) {
  	std::cout << "callerMember, v = " << v << '\n';
  }
};

int main() {
  X x;
  x.test(777);
}

http://ideone.com/fork/mKrX20

Można powiedzieć, że funkcje składowe mają niejawny argument będący wskaźnikiem na klasę. W takim razie Twoja funkcja wygląda tak:

void newFileOnPartition(MainWindow *object, const QFile &file)

Funkcja addFilesToTable ma parametr std::function<void(const QFile&)> - nie ma tam miejsca na przekazanie wskaźnika do obiektu. Dlatego za pomocą std::bind należy stworzyć obiekt funkcyjny, który będzie miał na stałe przypisany ten pierwszy, niejawny parametr. W Twoim przypadku będzie to this. Drugi parametr to już jawny parametr funkcji, który musi być podany podczas wywołania. std::bind z funkcji

void newFileOnPartition(MainWindow *object, const QFile &file)

robi:

void newFileOnPartition(const QFile &file)

z przypisanym na stałe parametrem object = this.
A to dokładnie to, czego oczekuje std::function<void(const QFile&)>.

Inną opcją jest użycie lambdy. W moim przykładnie ulegnie wtedy zmianie jedna linijka (8):

caller([this](int v) { callbackMember(v); }, v);

http://ideone.com/TmEl6c
Taka lambda po prostu wywołuje odpowiednią metodę (this należy podać w parametrach domknięcia, bo w dalszym ciągu jest on potrzebny jako ten niejawny argument, takie wywołanie można tez przedstawić jako this->callbackMember(v)) z podanym parametrem. Lambdę można podać bezpośrednio w wywołaniu funkcji, ponieważ może ona zostać skonwertowana do obiektu klasy std::function (ponieważ jest callable).
(Lambda dodana specjalnie dla wielbiciela lambd @Satireva :-P)

1

Zakładając że nie chcesz budować interfejsów ani ich implementować, proste rozwiązanie w stylu C++98 będzie wyglądać mniej więcej tak:

 
void MainWindow::addFilesToTable( EnumFileTable tableId, 
                                  std::function<void( const QFile& )> &functionOnFile )
{
    auto filePaths = QFileDialog::getOpenFileNames();

    for( const auto &filePath : filePaths )
    {
        QFile file( filePath );
        if( file.open( QIODevice::ReadOnly ) )
        {
            fileTables[tableId].addFile( filePath );
            functionOnFile( file );
            file.close();
        }
    }
}

struct MainWindowAddFile
{ 
  MainWindowAddFile(MainWindow *mainWindow): 
     m_mainWindow(mainWindow)
    {}
  void operator()(const QFile &file) const 
  { 
        m_mainWindow->newFileOnPartition(file);
  } 
private:
  MainWindow *m_mainWindow;
}; 

void MainWindow::newFileOnPartition( const QFile &file )
{
    hideInfo.freeSpace += file.size();
    ui->labFreeSpace->setText( "Total free space: " + QString::number( hideInfo.freeSpace ) );
}

void MainWindow::on_addFilesOnPartitionButton_clicked()
{
    MainWindowAddFile add(this);
    addFilesToTable( FILETABLE_FILES_ON_PARTITION, add );
}
0

@Endrju
Dziękuję za dokładne wytłumaczenie :)

Teraz tylko się upewnię, że dobrze zrozumiałem. Metoda on_addFilesOnPartitionButton_clicked() ma wyglądać teraz tak?

 
void MainWindow::on_addFilesOnPartitionButton_clicked()
{
    auto functionToCallOnFile = std::bind( &MainWindow::newFileOnPartition, 
                                           this, 
                                           std::placeholders::_1);
    
    addFilesToTable( FILETABLE_FILES_ON_PARTITION, functionToCallOnFile );
}

@vpiotr
Dzięki temu też się przyjże :)

0

@stryku, jeszcze zerknij do definicji funkcji std::mem_fn() i std::ptr_fun() z <functional>. Myślę że w kilku przypadkach może Cię ratować :-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1