PDA

View Full Version : Strange compilation errors when using "invokeMethod"



jcbaraza
7th December 2021, 20:46
Hi,

I'm renewing an old application by "threading" it. The idea is to deliver a (high) number of simulation tasks to the threads that the computer may have. After reading a lot about threading, I've used a QThreadPool in a "server" class to deliver the tasks. These tasks are implemented in a so-called "client" class. I also want to have information about the evolution of the thread operation, so the "server" class has a GUI where I show information about it, such as the status of the tasks (pending/in progress/completed), a progress bar, etc.

I've implemented the reporting from the threaded tasks through "invokeMethod", invoking in the "client" class two slots from the "server" class, in a similar way to the example in https://www.walletfox.com/course/qrunnableexample.php.

However, I'm getting some strange compiler errors:

* In the two invokeMethod calls ("no matching function for call to 'QMetaObject::invokeMethod(QWidget*&, ...").
* In "qobjectdefs.h" file ("no type named 'type' in struct std::enable_if<false, bool>'").
* In the moc file of the client's cpp file!!!

Probably all may be due to the same error (or errors), but I'm not able to catch. I want to remark that, although I comment the invokeMethod calls, the other errors persist...

In other posts I've seen that you suggested to delete the moc and ui files, the makefiles, etc. So, I've deleted completely the build folder, and recompiled the project as new. But the errors in the moc file do appear again.

Any idea? I enclose the h and cpp files for the "client" and "server" classes (although simplifying some irrelevant functions) in case they may help. If you consider necessary to upload all the application, please don't hesitate to ask, and I'll do.

Thanks!

Server.h



#ifndef SERVERMTH_H
#define SERVERMTH_H

#include <QFile>
#include <QFileInfo>
#include <QDialog>
#include <QMessageBox>
#include <QProcess>
#include <QProgressDialog>
#include <QString>
#include <QStringList>
#include <QThreadPool>
#include <QElapsedTimer>
#include "BasicDeclarations.h"
#include "ClientRunnable.h"

namespace Ui {
class ServerMTh;
}

class ServerMTh : public QDialog
{
Q_OBJECT

public:
explicit ServerMTh(QWidget *parent = nullptr, QString designpath = "", QString design = "",
int nbr_injections = 0, QStringList *macro_list = nullptr,
QStringList *trace_list = nullptr);
~ServerMTh();

private slots:
void confirmAbort();
void ClientStarted(int id_client);
void ClientTerminated(int id_client, int client_status);

private:
enum
{
INJECTIONNUMBER, INJECTIONSTATUS
};
#define SimulationPending 0
#define SimulationInProgress 1
#define SimulationTerminated 2
#define SimulationError 3

Ui::ServerMTh *ui;
bool ForceAbort;
bool Disconnecting;
int return_value;
int NbrInjections, i_progress;
int maxOperativeThreads, InjectingThreads;
int TotalSimulations, SimulationsFinished, SimulationsInProgress;
int *InjectionStatus;
QString Design_Path, Design;
QStringList *macroList, *traceList;
QThreadPool *ThreadPool;
QList<int> ErrorClients;
QElapsedTimer *timerStart, *timerStartSimulation;
qint64 EndOperationTime, EndSimulationTime;

void reject();
QString FormatTime(const qint64 time);
int WriteTimeReport(QString fich);
};

#endif // SERVERMTH_H


The two slots that should be invoked by the client are ClientStarted(int id_client) and ClientTerminated(int id_client, int client_status).

Server.cpp


#include <QDir>
#include <QTextStream>
#include "ServerMTh.h"
#include "ui_ServerMTh.h"


ServerMTh::ServerMTh(QWidget *parent, QString designpath, QString design, int nbr_injections, QStringList *macro_list, QStringList *trace_list) :
QDialog(parent),
ui(new Ui::ServerMTh)
{
ui->setupUi(this);

// GUI and other elements setup

// 2. Create runnables and launch to threads
for (int i_injection = 0; (i_injection < TotalSimulations); i_injection++)
{
ClientRunnable *Client = new ClientRunnable(this);// The idea is that the client knows the "owner" of ths slots to invoke
Client->ID = i_injection;
Client->Design = Design;
Client->MacroFile = macroList->at(i_injection);
ThreadPool->start(Client);
}
}

ServerMTh::~ServerMTh()
{
}

void ServerMTh::confirmAbort()
{
ui->progressCancel->setEnabled(false);
QString msg = "Abort injection. Are you sure?";
QMessageBox msgBox(this);
msgBox.setIcon(QMessageBox::Question);
msgBox.setText(msg);
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
if (msgBox.exec() == QMessageBox::Yes)
{
ui->progressInfo->setText("Please wait until injections in progress end...");
qApp->processEvents();

ThreadPool->clear();
ThreadPool->waitForDone(-1);
Disconnecting = true;
done(-1);
}
else
ui->progressCancel->setEnabled(true);

qApp->processEvents();
}

void ServerMTh::ClientStarted(int id_client)
{
// Update GUI
}

void ServerMTh::ClientTerminated(int id_client, int client_status)
{

// Check Modelsim start status
if (client_status == ClientRunnable::KOCLIENTSTART)
{
if (ErrorClients.contains(id_client))
{

// Same simulation has produced two errors => Terminate
ThreadPool->clear();
ThreadPool->waitForDone(-1);
Disconnecting = true;
done(-1);
}
else
{

// Add the task to the pool
ClientRunnable *Client = new ClientRunnable;
Client->ID = id_client;
Client->Design = Design;
Client->MacroFile = macroList->at(id_client);
ThreadPool->start(Client);
}
}
else
{
if (ErrorClients.contains(id_client))
{

// Remove erroneous simulation from the list
ErrorClients.removeAt(ErrorClients.indexOf(id_clie nt));
}

// Update GUI

if ((TotalSimulations - (SimulationsFinished + SimulationsInProgress)) == 0)
{
ThreadPool->waitForDone(-1);
Disconnecting = true;
done(0);
}
}
}

void ServerMTh::reject()
{
if (!Disconnecting)
confirmAbort();
}

QString ServerMTh::FormatTime(const qint64 time)
{
return QString("%1:%2:%3").arg((time / 3600000), 2, 10, QChar('0')).arg(((time / 60000) % 60), 2, 10, QChar('0')).arg(((time / 1000) % 60), 2, 10, QChar('0'));
}

int ServerMTh::WriteTimeReport(QString fich)
{
return 0;
}


Client.h


#ifndef CLIENT_RUNNABLE_H
#define CLIENT_RUNNABLE_H

#include <QObject>
#include <QProcess>
#include <QRunnable>
#include <QString>

class ClientRunnable : public QRunnable
{
Q_OBJECT

public:
enum
{
OKCLIENTSTART, KOCLIENTSTART
};

ClientRunnable(QWidget *parent = nullptr);
int ID;
QString MacroFile, Design;

private:
QWidget *Parent;
QString LogFile, TraceFile;
int Status;
QProcess *proc;

int CheckLog();
int FindTrace();
int CleanTrace();

protected:
void run();
};

#endif // CLIENT_RUNNABLE_H


Client.cpp



#include <QArgument>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QMetaObject>
#include <QStringList>
#include "ClientRunnable.h"

ClientRunnable::ClientRunnable(QWidget *parent)
{
Parent = parent;
Status = OKCLIENTSTART;
}

void ClientRunnable::run()
{
// Compìler error HERE:
QMetaObject::invokeMethod(Parent, "ClientStarted", Qt::QueuedConnection, Q_ARG(int, ID));
// Note that "Parent" is the server, that is, the owner of the slot invoked

// 1. Create process and run
QStringList pars;
pars << ...;
proc = new QProcess;
if (proc->execute("vsim.exe", pars) < 0)
Status = KOCLIENTSTART;

// 2. Check log file
if (Status == OKCLIENTSTART)
{
if (CheckLog() < 0)
Status = KOCLIENTSTART;
}

// 3. Compress trace file
if (Status == OKCLIENTSTART)
{
if (FindTrace() < 0)
Status = KOCLIENTSTART;

if (Status == OKCLIENTSTART)
{
if (TraceFile != "")
{
if (CleanTrace() < 0)
Status = KOCLIENTSTART;
}
else
Status = KOCLIENTSTART;
}
}

// Compìler error HERE too:
QMetaObject::invokeMethod(Parent, "ClientTerminated", Qt::QueuedConnection, Q_ARG(int, ID), Q_ARG(int, Status));

if (Status == OKCLIENTSTART)
exit(0);
else
exit(-1);
}

int ClientRunnable::CheckLog()
{
return 0;
}

int ClientRunnable::FindTrace()
{
return 0;
}

int ClientRunnable::CleanTrace()
{
return 0;
}

d_stranz
8th December 2021, 00:15
Your ClientRunnable class declares the member variable "Parent" as type QWidget *. QWidget has no slot named "ClientStarted", so the compiler is telling you that. If you want this to work, you need to qobject_cast<ServerMTh *>( parent ) in the ClientRunnable constructor and store it explicitly as ServerMTh * Parent.

In line 79 of ServerMTh.cpp above, you create a ClientRunnable instance with no parent. This will cause a crash as soon as run() executes since Parent will be uninitialized.

jcbaraza
8th December 2021, 12:29
Your ClientRunnable class declares the member variable "Parent" as type QWidget *. QWidget has no slot named "ClientStarted", so the compiler is telling you that. If you want this to work, you need to qobject_cast<ServerMTh *>( parent ) in the ClientRunnable constructor and store it explicitly as ServerMTh * Parent.

In line 79 of ServerMTh.cpp above, you create a ClientRunnable instance with no parent. This will cause a crash as soon as run() executes since Parent will be uninitialized.

Thanks a lot for your reply @d_stranz, mainly for the second detail; I had skipped...

Regarding the first solution, if I'm not wrong, you mean that I should change line 11 of Client.cpp to "Parent = qobject_cast<ServerMTh *>( parent );", and consequently change the declaration of Parent in Client.h (in line 24) to "QServerMTh * Parent;", is that right? Well, when I do so, I get two new error messages in the Qt Editor:
* one in Client.cpp ("use of undeclared identifier ServerMTh"), and
* another one in Client.h ("unknown type name ServerMTh").

Thus, I need to #include "ServerMTh.h" in Client.h (in line 8). This makes the aforementionned errors dissapear, but two new appear in Client.h:
* "unterminated conditional directive" in line 1, and
* "ServerMTh does not name a type" in line 24 (the updated declaration of Parent: ServerMTh *Parent;

Obviously I've created a circular dependency between classes ServerMTh and ClientRunnable. I've been struggling with it, and I think that I've maybe solved (see changes below), with forwarded class declarations in both classes, and including both headers in the cpp files.

Server.h


#ifndef SERVERMTH_H
#define SERVERMTH_H

#include <QFile>
#include <QFileInfo>
#include <QDialog>
#include <QMessageBox>
#include <QProcess>
#include <QProgressDialog>
#include <QString>
#include <QStringList>
#include <QThreadPool>
#include <QElapsedTimer>
#include "BasicDeclarations.h"
class ClientRunnable;
namespace Ui {
class ServerMTh;
}


Server.cpp


#include <QDir>
#include <QTextStream>
#include "ServerMTh.h"
#include "ui_ServerMTh.h"
#include "ClientRunnable.h"

ServerMTh::ServerMTh(QWidget *parent, QString designpath, QString design,
int nbr_injections, QStringList *macro_list,
QStringList *trace_list) :


Client.h


#ifndef CLIENT_RUNNABLE_H
#define CLIENT_RUNNABLE_H

#include <QObject>
#include <QProcess>
#include <QRunnable>
#include <QString>
class ServerMTh;
class ClientRunnable : public QRunnable
{
Q_OBJECT

public:
enum
{
OKCLIENTSTART, KOCLIENTSTART
};

ClientRunnable(QWidget *parent = nullptr);
int ID;
QString MacroFile, Design;

private:
ServerMTh *Parent;


Client.cpp


#include <QArgument>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QMetaObject>
#include <QStringList>
#include "ClientRunnable.h"
#include "ServerMTh.h"

ClientRunnable::ClientRunnable(QWidget *parent)
{
Parent = qobject_cast<ServerMTh *>(parent);
Status = OKCLIENTSTART;
}



Now, I don't get any compiler errors in these classes. But I'm still getting six errors in the moc_ClientRunnable.cpp. I'm completely lost. Can anyone help me, please? I list the errors an the lines where they appear after the code, just in case this can help someone to guess what is/are the problems.

Thanks again!



/************************************************** **************************
** Meta object code from reading C++ file 'ClientRunnable.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.13.0)
**
** WARNING! All changes made in this file will be lost!
************************************************** ***************************/

#include <memory>
#include "../../VFIT_MTh2/ClientRunnable.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'ClientRunnable.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.13.0. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_ClientRunnable_t {
QByteArrayData data[1];
char stringdata0[15];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_O FFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_ClientRunnabl e_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_ClientRunnable_t qt_meta_stringdata_ClientRunnable = {
{
QT_MOC_LITERAL(0, 0, 14) // "ClientRunnable"

},
"ClientRunnable"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_ClientRunnable[] = {

// content:
8, // revision
0, // classname
0, 0, // classinfo
0, 0, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount

0 // eod
};

void ClientRunnable::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
Q_UNUSED(_o);
Q_UNUSED(_id);
Q_UNUSED(_c);
Q_UNUSED(_a);
}

QT_INIT_METAOBJECT const QMetaObject ClientRunnable::staticMetaObject = { {
&QRunnable::staticMetaObject,
qt_meta_stringdata_ClientRunnable.data,
qt_meta_data_ClientRunnable,
qt_static_metacall,
nullptr,
nullptr
} };


const QMetaObject *ClientRunnable::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *ClientRunnable::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_ClientRunnable.stringdata0))
return static_cast<void*>(this);
return QRunnable::qt_metacast(_clname);
}

int ClientRunnable::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QRunnable::qt_metacall(_c, _id, _a);
return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE


* 'staticMetaObject' is not a member of 'QRunnable' : moc_ClientRunnable.cpp (line 67)

In member function 'virtual const QMetaObject* ClientRunnable::metaObject() const' : moc_ClientRunnable.cpp
* 'QScopedPointer <QObjectData> QObject::d_ptr' is protected within this context : moc_ClientRunnable.cpp (line 78)

In file included from C:\Qt\Qt5.13.0\5.13.0\mingw73_64\include\QtCore/QObject:1:0 : QObject (line 1)
from debug\../../VFIT_MTh2/ClientRunnable.h:4 : ClientRunnable.h (line 4)
from debug\moc_ClientRunnable.cpp:10 : moc_ClientRunnable.cpp (line 10)
declared protected here
QScopedPointer<QObjectData> d_ptr; : qobject.h (line 436)
* invalid use of non-static data member 'QObject::d_ptr' : moc_ClientRunnable.cpp (line 78)

In file included from C:\Qt\Qt5.13.0\5.13.0\mingw73_64\include\QtCore/QObject:1:0 : QObject (line 1)
from debug\../../VFIT_MTh2/ClientRunnable.h:4 : ClientRunnable.h (line 4)
from debug\moc_ClientRunnable.cpp:10 : moc_ClientRunnable.cpp (line 10)
declared here
QScopedPointer<QObjectData> d_ptr; : qobject.h (line 436)
* 'QScopedPointer <QObjectData> QObject::d_ptr' is protected within this context : moc_ClientRunnable.cpp (line 78)

In member function 'virtual void* ClientRunnable::qt_metacast(const char*)': moc_ClientRunnable.cpp
* 'qt_metacast' is not a member of 'QRunnable' : moc_ClientRunnable.cpp (line 86)
return QRunnable::qt_metacast(_clname);

In member function 'virtual int ClientRunnable::qt_metacall(QMetaObject::Call, int, void**)': moc_ClientRunnable.cpp
* 'qt_metacall' is not a member of 'QRunnable' : moc_ClientRunnable.cpp (line 91)
_id = QRunnable::qt_metacall(_c, _id, _a);

d_stranz
8th December 2021, 15:53
You have included the Q_OBJECT macro in the declaration of class ClientRunnable, but the base class QRunnable is not derived from QObject. Therefore, all of the meta-object boilerplate that the Q_OBJECT macro inserts (and which the moc compiler looks for) can't be compiled because the required QObject base class code to support it isn't there.

If you need support for signals and slots in ClientRunnable (I don't see that you do), then you can derive it from both QObject and QRunnable. If you don't, then remove the Q_OBJECT macro, delete the moc_ClientRunnable.* files, and re-run qmake (if you are using Qt Creator) or a rebuild all if you are using Visual Studio.

jcbaraza
8th December 2021, 17:23
Yes! Just another silly mistake. Most of my classes haven't the Q_OBJECT macro, so I don't know why I put it there...

Thanks a lot!