In my work project I have run into a particularly strange behaviour. During runtime, when calling QMetaObject::invokeMethod, I get a print indicating that the program is trying to find the specified method from QObject, instead of the actual class that the pointer I've given it points to. Even more strange is the fact that I made a mockup that, to my mind functions largely the same way as the code proper, with the difference that the mockup actually does what is expected. I can't find meaningful differences between the two nor figure out why my code fails to function as it should.
Please excuse some poor practices in the code shown. Due to reasons unrelated to this issue I've been forced to edit portions of the code, primarily names of classes and libraries, and haven't done a very good job in maintaining the quality present in the actual code. This has no effect in actual functionality, only readability.
I will provide the code of the mockup upon request, but this code is actually the one I'm having trouble with so I feel addingg that would just lead to unneccessary bloat.
EDIT
I was a dumb-dumb and forgot to include the error message. Please note that this too is similarily edited as the code base (that is, names of classes and some function names have been changed)
QMetaObject::invokeMethod: No such method QObject::simpleRx(QString,int,client*)
EDIT #2 Okay, so the source of the issue was the lack of Q_OBJECT macro in Host. Now I'm getting an "QMetaMethod::invoke: Unable to handle unregistered datatype 'int&'" error which I will look into next.
main.cpp
#include "maincontroller.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MainController mc(argc, argv);
return a.exec();
}
maincontroller.h
class MainController : public QObject
{
Q_OBJECT
private:
//attributes
client* comm;
host* host;
bool allOk;
QStringList argList;
int parseArguments(QStringList &arguments, int argc, char *argv[]);
public:
//methods
explicit MainController(int argc, char *argv[], QObject *parent = nullptr);
};
#endif // MAINCONTROLLER_H
maincontroller.cpp
#include "client.h"
#include "host.h"
#include <QCoreApplication>
MainController::MainController(int argc, char *argv[], QObject *parent) : QObject(parent)
{
if(true)
{
host=new host();
comm=new client(host, "main", this,0);
//connect(comm, &client::killSignal, this, &MainController::quit);
}
else
{
exit(EXIT_FAILURE);
}
}
client.h
#define client_H
#include <QObject>
class QUuid;
class host;
class client : public QObject
{
Q_OBJECT
public:
explicit client(host* givenHost, QString givenId, QObject* givenOwner, int givenPriority, QObject *parent=nullptr);
signals:
void killSignal();
public slots:
private:
//attributes
//pointer to the instance that is using this instance of client
QObject* owner;
QString simpleId; //placeholder attribute until id mess is sorted
host* host;
QHash<QString, const char*>* handlers;
//methods
public:
//attributes
//methods
explicit client(host* givenHost, QString givenId, QObject* givenOwner, int givenPriority, QObject *parent=nullptr);
int simpleTx(QString msg, QString msgName, QString simpleTarget, client* clientPtr=nullptr);
int registerMe(QString threadName);
};
#endif // client_H
client.cpp
#include "host.h"
#include "globals.h"
#include<QUuid>
#include <QHash>
#include<QJsonObject>
#include<QJsonDocument>
#include <QMetaObject>
#include<QDebug>
client::client(host* givenHost, QString givenId, QObject* givenOwner, int givenPriority, QObject *parent) : QObject(parent),
host(givenHost),
handlers(new QHash<QString, const char*>),
owner(givenOwner),
simpleId(givenId),
priority(givenPriority)
{
id = new QUuid();
id->createUuid();
registerMe(givenId);
}
//registers this instance of client to host and sets simpleId to threadName
int client::registerMe(QString threadName)
{
QJsonObject payload;
payload.insert(SIMPLEID, threadName);
payload.insert(PRIORITY, priority);
payload.insert(ID, id->toString(QUuid::StringFormat::WithoutBraces));
simpleId = threadName;
simpleTx(JSONtoStr(payload),IPCREGISTRATIONHEADER, HOST, this);
return ALLCLEAR;
}
int client::simpleTx(QString msg, QString msgName, QString SimpleTarget, client* clientPtr)
{
int transmitReturnValue = 420;
QJsonObject payload = StrToJSON(msg);
QString fullMsg = JSONtoStr(JSONify(simpleId, SimpleTarget, msgName, payload));
if(QMetaObject::invokeMethod(host, "simpleRx", Qt::QueuedConnection, Q_ARG(QString, fullMsg), Q_ARG(int, transmitReturnValue), Q_ARG(client*, nullptr)))
{
return ALLCLEAR;
}
else
{
return INVOKEERROR;
}
}
int client::registerHandler(QString msgName, const char* method)
{
handlers->insert(msgName, method);
return ALLCLEAR;
}
int client::getPriority()
{
return priority;
}
QJsonObject client::JSONify(QString src, QString dst, QString msgName, QJsonObject payload)
{
QJsonObject header;
QJsonObject msg;
header.insert(SRC, src);
header.insert(DST, dst);
header.insert(MSGNAME, msgName);
msg.insert(HEADER, header);
msg.insert(PAYLOAD, payload);
return msg;
}
QString client::JSONtoStr(QJsonObject json)
{
QJsonDocument doc(json);
QString msgString(doc.toJson(QJsonDocument::Compact));
return msgString;
}
QJsonObject client::StrToJSON(QString jsonString)
{
QJsonObject obj;
QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8());
// check validity of the document
if(!doc.isNull())
{
if(doc.isObject())
{
obj = doc.object();
}
else
{
qDebug() << "Document is not an object";
}
}
else
{
qDebug() << "Invalid JSON...\n" << jsonString;
}
return obj;
}
host.h
#define host_H
#include <QObject>
class client;
class host : public QObject
{
signals:
public slots:
private:
//attributes
QHash<QUuid*, client*>* registeredClients;
QHash<QString, client*>* simpleRegisteredClients;
QList<int> priorities;
//methods
int rx(QString jsonString, client* clientPtr);
Q_INVOKABLE void simpleRx(QString jsonString, int &transmitreturnvalue, client* clientPtr=nullptr);
QJsonObject StrToJSON(QString jsonString);
public:
//attributes
//methods
explicit host(QObject *parent = nullptr);
};
#endif // host_H
host.cpp
#include "clent.h"
#include "globals.h"
#include<QJsonDocument>
#include<QJsonObject>
#include<QDebug>
host::host(QObject *parent) : QObject(parent)
{
}
void host::simpleRx(QString jsonString, int &transmitReturnValue, clent* clientPtr)
{
QString msgName = StrToJSON(jsonString).value(HEADER).toObject().value(MSGNAME).toString();
int rxret = 1;
if(msgName==REGISTRATIONHEADER)
{
QString clientName = StrToJSON(jsonString).value(PAYLOAD).toObject().value(SIMPLEID).toString();
simpleRegisteredClients->insert(clientName, clientPtr);
if(clientPtr->getPriority()!=0)
{
priorities.push_back(clientPtr->getPriority());
}
transmitReturnValue = ALLCLEAR;
}
else if(msgName==ACCEPTDEATH)
{
QString simpleName = StrToJSON(jsonString).value(PAYLOAD).toObject().value(SIMPLEID).toString();
simpleRegisteredClients->remove(simpleName);
}
else if(msgName==KILLMESSAGE && StrToJSON(jsonString).value(HEADER).toObject().value(DST).toString()==ALL)
{
std::sort(priorities.begin(), priorities.end());
QHash<QString, clent*>::iterator i;
while(!priorities.empty())
{
int currentPriority = priorities.takeLast();
for (i = simpleRegisteredClients->begin(); i != simpleRegisteredClients->end(); ++i)
{
if(i.value()->getPriority()==currentPriority)
{
QMetaObject::invokeMethod(i.value(), "rx", Qt::ConnectionType::QueuedConnection,
Q_RETURN_ARG(int, rxret), Q_ARG(QString, jsonString));
}
}
}
QJsonObject finalMsg;
QJsonObject payload;
QJsonObject header;
QString finalJsonString;
header.insert(SRC, HOST);
header.insert(MSGNAME, FINALMESSAGE);
finalMsg.insert(HEADER, header);
finalMsg.insert(PAYLOAD, payload);
for (i = simpleRegisteredClients->begin(); i != simpleRegisteredClients->end(); ++i)
{
if(i.value()->getPriority()==0)
{
QMetaObject::invokeMethod(i.value(), "rx", Qt::ConnectionType::QueuedConnection,
Q_RETURN_ARG(int, rxret), Q_ARG(QString, finalJsonString));
}
}
}
else
{
QString clientName = StrToJSON(jsonString).value(HEADER).toObject().value(DST).toString();
bool invokeSuccesful=false;
invokeSuccesful = QMetaObject::invokeMethod(simpleRegisteredClients->value(clientName), "rx",
Qt::ConnectionType::QueuedConnection, Q_RETURN_ARG(int, rxret), Q_ARG(QString, jsonString));
if(!invokeSuccesful)
{
qDebug() << "Invoke was not succesful";
transmitReturnValue = INVOKEERROR;
}
}
transmitReturnValue = ALLCLEAR;
}
QJsonObject host::StrToJSON(QString jsonString)
{
QJsonObject obj;
QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8());
// check validity of the document
if(!doc.isNull())
{
if(doc.isObject())
{
obj = doc.object();
}
else
{
qDebug() << "Document is not an object";
}
}
else
{
qDebug() << "Invalid JSON...\n" << jsonString;
}
return obj;
}
CMakeLists.txt
project(myProject LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network REQUIRED)
set(srcPath ${CMAKE_CURRENT_LIST_DIR}/sources)
set(hdrPath ${CMAKE_CURRENT_LIST_DIR}/headers)
set(extLib ${CMAKE_CURRENT_SOURCE_DIR}/../../externals/path_to/so_file)
set(libIncludes ${CMAKE_CURRENT_SOURCE_DIR}/../../externals/path_to/library/source/)
set(fileList
${srcPath}/main.cpp
${srcPath}/client.cpp
${srcPath}/host.cpp
${srcPath}/maincontroller.cpp
${hdrPath}/maincontroller.h
${hdrPath}/globals.h
${hdrPath}/client.h
${hdrPath}/host.h
)
add_executable(myProject
${fileList}
)
set_target_properties(myProject PROPERTIES
AUTOMOC ON
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
VERSION 0.9.0
EXPORT_NAME "myProject"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/build/archive"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/build/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/build/run"
)
target_include_directories(myProject PRIVATE headers/)
target_include_directories(myProject PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/headers ${libIncludes})
target_link_libraries(myProject PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network ${extLib})
I'm running Qt 6.1.0 on an Ubuntu 20.04
Please ask for any further details you need, and I will provide them if I can.
Your help is much appreciated.