I got a runtime crash when registering a QList (CustomType is a Q_GADGET generate by the repc (Qt remote object compiler)).
Actually I am trying to expose a list of custom type using Qt Remote Object.
file.rep :
POD Service(QString name, QUrl enpoint)
POD Services(QList<Service> svcs)
class ROObject
{
PROP(bool state = false);
PROP(Services services);
}
main.cpp:
#include <QCoreApplication>
#include <QTimer>
#include <QRemoteObjectHost>
#include "rotest.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaType<QList<Service>>();
ROTest t;
QList<Service> l;
l.push_back(Service("houssem", QUrl("local:houssem")));
l.push_back(Service("houss", QUrl("local:houss")));
Services ss(l);
t.setServices(ss);
QRemoteObjectHost host;
host.setHostUrl(QUrl("local:s"));
host.enableRemoting(&t);
qDebug() << "Services : " << t.services().svcs().count();
return a.exec();
}
error:
Thank you for your help.
Qt doesn't know how to serialize your custom types.
In addition to registering it as a meta type, you need to implement and register stream operators for it:
void qRegisterMetaTypeStreamOperators(const char *typeName)
Registers the stream operators for the type T called typeName.
Afterward, the type can be streamed using QMetaType::load() and
QMetaType::save(). These functions are used when streaming a QVariant.
qRegisterMetaTypeStreamOperators<MyClass>("MyClass");
// The stream operators should have the following signatures:
QDataStream &operator<<(QDataStream &out, const MyClass &myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);
Also, what you actually need to register as a meta type is Service and not QList<Service>. Once service is registered and its streaming operators are implemented you should be all set, Qt knows how to handle the QList part.
Related
I try to implement GitHub authentication from my Qt application using Access Token. I can connect to GitHub using Postman without any problem by providing URL (in my case this is https://api.github.com/notifications) and access token I've generated on the GitHub (Settings/Developer settings/Personal access tokens) https://github.com/settings/tokens. Now I want to do the same using Qt but can't find examples how to do that. There is example for Reddit( https://doc.qt.io/qt-5/qtnetworkauth-redditclient-example.html) in the Qt documentation but it uses some redirect algorithm, not access token:
auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this);
oauth2.setReplyHandler(replyHandler);
oauth2.setAuthorizationUrl(QUrl("https://www.reddit.com/api/v1/authorize"));
oauth2.setAccessTokenUrl(QUrl("https://www.reddit.com/api/v1/access_token"));
I have no clue how to do the same using provided Access Token.
If you are going to use the personal token then it is not necessary to implement the OAuth/OAuth2 logic since the first part of the transaction is to obtain the token but the OP already has it. If you have the token then it is only necessary to request the rest API:
#include <QByteArray>
#include <QCoreApplication>
#include <QJsonDocument>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const QString username = "<username>";
const QString token = "<token>";
const QByteArray basic_authorization = QByteArray("Basic ") + (username + ":" + token).toUtf8().toBase64();
QNetworkRequest request;
request.setRawHeader(QByteArrayLiteral("Authorization"), basic_authorization);
QUrl url("https://api.github.com/notifications");
request.setUrl(url);
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(request);
QObject::connect(reply, &QNetworkReply::finished, [reply](){
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
qDebug() << doc;
QTimer::singleShot(1000, &QCoreApplication::quit);
});
return a.exec();
}
Question: Is there a way to check at compile-time if qRegisterMetaType<T>() was called for a custom type T?
The custom type T needs to be registered in Qt meta-type system in order to be used in e.g. queued connections.
If such a connection is made, and a signal triggered, the runtime warning will be shown:
QObject::connect: Cannot queue arguments of type 'T'
(Make sure 'T' is registered using qRegisterMetaType().)
This is hard to track, so I would prefer to check this at compile-time. Is that in any way possible?
(I understand that if it was possible, it would probably already be a part of Qt Framework itself, but maybe...?)
Note: I know I can check if a type was declared as metatype (Check if type is declared as a meta type system (for SFINAE)), but this doesn't solve my problem.
The code example would be:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaMethod>
#include <QObject>
#include <QThread>
#include <QTimer>
struct Payload {
Payload() = default;
};
// Type is declared as metatype
Q_DECLARE_METATYPE(Payload)
class ObjectOne : public QObject {
Q_OBJECT
public:
using QObject::QObject;
void emitPayloadChanged() { Payload p; emit payloadChanged(p); }
signals:
void payloadChanged(const Payload& p);
};
class ObjectTwo : public QObject {
Q_OBJECT
public:
using QObject::QObject;
void handlePayload(const Payload& p) { qDebug() << "handling payload"; }
};
int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv);
// Uncommenting the following line fixes the runtime warning
// qRegisterMetaType<Payload>();
QThread t1, t2;
ObjectOne o1;
o1.moveToThread(&t1);
ObjectTwo o2;
o2.moveToThread(&t2);
t1.start();
t2.start();
QObject::connect(&o1, &ObjectOne::payloadChanged, &o2, &ObjectTwo::handlePayload);
QTimer::singleShot(0, &o1, [&] { QMetaObject::invokeMethod(&o1, &ObjectOne::emitPayloadChanged); });
return app.exec();
}
#include "main.moc"
I want to get the value of the SO_RCVBUF socket option used by Qt to be sure that it uses by default the system value (that I changed).
But the following piece of code returns an "Invalid" QVariant:
QUdpSocket socket;
qDebug() << socket.socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
Does it mean that the socketOption() Qt method only get the value if it has been set with setSocketOption()?
Or did I make a mistake?
In order to obtain the socket information, then the native socket must have been created, that is, obtain a socketDescriptor() other than -1, but in your case it is not connected causing that value not to be read, returning a invalid QVariant.
The solution is to connect the socket and analyze that the socket is valid to obtain the desired information:
#include <QCoreApplication>
#include <QTimer>
#include <QUdpSocket>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QUdpSocket socket;
QObject::connect(&socket, &QAbstractSocket::stateChanged, [&socket](){
if(socket.socketDescriptor() != -1){
qDebug() << socket.socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
// kill application
QTimer::singleShot(1000, &QCoreApplication::quit);
}
});
socket.bind(QHostAddress::LocalHost, 1234);
return a.exec();
}
Output:
QVariant(int, 212992)
I have studied the qt documentation of qRegisterMetaType() where it says that this function must be called before the corresponding type can be used in signal/slot mechanism. However I couldn't find any code example where this has to be done by hand.
This page states, that the registration is done automatically by the moc if it can determine that the type may be registered as meta-type. It looks like this is right, because I tested QSignalSpy, QObject::connect() (direct and queued connection) and QVariant - with just using Q_DECLARE_METATYPE(type) and none of them needed a explicit call to qRegisterMetaType to work.
So my question is: when do I have to call qRegisterMetaType(), because otherwise the code won't work?
The Qt docs say that Q_DECLARE_METATYPE is necessary in case one has a connect being a queued connection.
Adding a Q_DECLARE_METATYPE() makes the type known to all template
based functions, including QVariant. Note that if you intend to use
the type in queued signal and slot connections or in QObject's
property system, you also have to call qRegisterMetaType() since the
names are resolved at runtime.
For this I build a small testing app, that exemplifies the behavior.
Just try to remove the Q_DECLARE_METATYPE(Message) and watch the warnings and output change. In case of the normal connect the macro seems to be unnecessary.
main.cpp
#include <QApplication>
#include <QThread>
#include "MyHeaderView.h"
Q_DECLARE_METATYPE(Message);
int main(int argc, char **args)
{
QApplication app(argc, args);
{
TestObject sender;
TestObject receiver;
QObject::connect(&sender, &TestObject::sendMessage, &receiver, &TestObject::onMessage);
sender.emitMessage(1, 2);
}
// This requires Q_DECLARE_METATYPE(Message);
QThread workerThread;
TestObject sender2;
TestObject receiver2;
receiver2.moveToThread(&workerThread);
workerThread.start();
QObject::connect(&sender2, &TestObject::sendMessage, &receiver2, &TestObject::onMessage, Qt::ConnectionType::QueuedConnection);
sender2.emitMessage(3, 4);
app.exec();
}
TestObject.h
#pragma once
#include <QObject>
#include <QDebug>
struct Message
{
int x;
int y;
};
class TestObject : public QObject
{
Q_OBJECT
public:
void emitMessage(int x, int y) { emit sendMessage(Message{ x,y }); }
signals:
void sendMessage(const Message&);
public slots:
void onMessage(const Message& m) { qDebug() << m.x << m.y; }
};
I have a string that I need at various points in my program. I know that Qt can manage image resources, but I need similar functionality for a couple of strings. Currently I'm using a string resource class, which is a sloppy solution.
class StringRes {
public:
static const QString& appName() { return _appName; }
static const QString& appVersion() { return _appVersion; }
private:
static const QString _appName;
static const QString _appVersion;
};
Besides, this solution causes a segfault at a certain point in my code.
_fileStream << QString("This is ")
+ StringRes::appName()
+ " "
+ StringRes::appVersion()
+ " reporting for duty.\n";
How do Qt programmers (or C++ programmers in general) manage their string resources?
For storing just application's name and version and organization's name and domain you can use QCoreApplications's properties applicationName, applicationVersion, organizationDomain and organizationName.
I usually set them in main() function:
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// These functions are member of QCoreApplication, QApplication's
// parent class:
app.setApplicationName("My Application");
app.setApplicationVersion("3.5.2");
app.setOrganizationName("My Company, or just My Name");
app.setOrganizationDomain("http://example.com/");
MainWindow window;
window.show();
return app.exec();
}
And I can use them to show a nice about message:
#include "MainWindow.h"
#include <QCoreApplication>
...
// Slot called when ? -> About menu is clicked.
void MainWindow::on_aboutAction_triggered()
{
QString message = tr("<strong>%1</strong> %2<br />"
"Developed by %3")
.arg(QCoreApplication::applicationName())
.arg(QCoreApplication::applicationVersion())
.arg(QString("%2")
.arg(QCoreApplication::organizationDomain())
.arg(QCoreApplication::organizationName()))
;
QMessageBox::about(this, tr("About"), message);
}