Передать указатель функции на метод другого класса - PullRequest
0 голосов
/ 31 марта 2020

Я работаю над своим первым проектом C ++. У меня есть 2 класса: 1 для взаимодействия с sqlite db, другой для главного окна qt. В основном я создаю новое окно. В конструкторе окна я хотел бы загрузить содержимое базы данных и отобразить его в QtWidget.

Так что, если я хорошо понимаю, функция обратного вызова sqlite будет вызываться для каждой строки, которую возвращает sqlite3_exe c. Я сделал функцию select_all в классе базы данных, которая принимает функцию обратного вызова в качестве аргумента, поэтому я смогу использовать одну и ту же функцию sql для отображения / использования данных различными способами.

 #include <cstdio>
#include <iostream>
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <qmainwindow.h>
#include <qstandarditemmodel.h>
#include <sqlite3.h>
#include <string>

using namespace std;

class Database {
public:
  sqlite3* db;
  Database() {db = create_or_open_database();}

  sqlite3* create_or_open_database()
  {
    sqlite3 *db = NULL;
    const char *query;
    int ret = 0;
    char *errorMsg = 0;

    ret = sqlite3_open("expense.db", &db);
    query = "CREATE TABLE IF NOT EXISTS EXPENSES(NAME TEXT KEY NOT NULL, AMOUNT INT NOT NULL, TAG TEXT, SUBTAG TEXT, DATE CHAR(10) NOT NULL);";
    ret = sqlite3_exec(db, query, callback, 0, &errorMsg);
    return db;
  }

  void select_all(int (*f)(void*, int, char**, char**)){
    int ret = 0;
    char *errorMsg = 0;

    const char *query = "SELECT * FROM EXPENSES";
    ret = sqlite3_exec(db, query, (*f), 0, &errorMsg);
  }
};


class MainWindow
{
public:
  QWidget window;
  Database expenses;
  QTreeView *navigateView = new QTreeView;
  QTreeView *expensesList = new QTreeView;
  QPushButton *newButton = new QPushButton;
  QVBoxLayout *mainVLayout = new QVBoxLayout;
  QHBoxLayout *listHLayout = new QHBoxLayout;
  QStandardItemModel *expensesModel = new QStandardItemModel;

  MainWindow()
  {
    QSizePolicy navigateSize(QSizePolicy::Preferred, QSizePolicy::Preferred);
    QSizePolicy expenseListSize(QSizePolicy::Preferred, QSizePolicy::Preferred);
    navigateSize.setHorizontalStretch(1);
    navigateView->setSizePolicy(navigateSize);
    expenseListSize.setHorizontalStretch(2);
    expensesList->setSizePolicy(expenseListSize);
    newButton->setText("New");
    listHLayout->addWidget(navigateView);
    listHLayout->addWidget(expensesList);
    mainVLayout->addLayout(listHLayout);
    mainVLayout->addWidget(newButton);
    window.setLayout(mainVLayout);

    // int (MainWindow::*foo)(void*, int, char**, char**) = &MainWindow::display_expenses_in_list;
    // expenses.select_all(foo);
    expenses.select_all(this->display_expenses_in_list);
  }

  int display_expenses_in_list(void *NotUsed, int argc, char **argv, char **azColName)
  {
    QStringList list = {"Name", "Amount (€)", "Tag", "Subtag", "Date"};
    this->expensesModel->setVerticalHeaderLabels(list);
    // here I'll create items and add them to the QTreeView
    return 0;
  }
};    
int main(int argc, char* argv[])
{
  QApplication app(argc, argv);
  MainWindow ui;
  ui.window.show();

  return app.exec(); 
}

С этим кодом я получаю reference to a non-static member function must be called [bound_member_function]

Если гуглил и попытался, я думаю, создать указатель на функцию foo, которая указывает на функцию обратного вызова (строки, которые в данный момент комментируются). Я получаю это: Cannot initialize a parameter of type 'int (*)(void *, int, char **, char **)' with an lvalue of type 'int (MainWindow::*)(void *, int, char **, char **)' [init_conversion_failed]

Если я наберу display_expenses_in_list stati c, тогда я не смогу редактировать модель затрат ...

Извините за грязный код.

Любая помощь будет очень признательна :)

Ответы [ 2 ]

0 голосов
/ 31 марта 2020

Проблема:

Проблема в том, что ваша display_expenses_in_list(...) функция является функцией-членом класса. Поэтому вам нужно использовать указатель на функцию-член, а не указатель на функцию, потому что она всегда должна вызываться для экземпляра класса, членом которого она является - однако библиотека sqlite будет принимать только функцию void* указатель.

Проверьте эту статью: https://isocpp.org/wiki/faq/pointers-to-members

Исправление:

Современный C ++, чтобы спасти здесь. Завершите весь вызов функции-члена класса в лямбду с экземпляром класса в области видимости, затем передайте указатель на эту новую анонимную функцию в качестве обратного вызова.

Проверьте ответ об этом стеке потока, показывая, как это сделать (скопировано ниже): { ссылка }

Пример (скопировано из связанного ответа):

if (sqlite3_exec(this->db, this->SQL_SELECT_READINGS_QUERY, 
    +[](void* instance, int x, char** y, char** z) {
        return static_cast<dataSend_task*>(instance)->callback(x, y, z);
    },
    this,
    &err))
{
    /* whatever */
}
0 голосов
/ 31 марта 2020

Ключом здесь является void* аргумент sqlite3_exec. Теперь вы проходите 0, но вместо этого вам нужно пройти this.

Теперь вы можете сделать display_expenses_in_list stati c. Этот NotUsed параметр затем используется. Вам просто нужно привести его обратно к MainWindow* и использовать вместо this.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...