Pass opaque JavaScript type to and from C++?
-
wrote on 3 Sept 2019, 23:28 last edited by
I want to create a JavaScript object in QML, give it to a C++ module for storage, and retrieve it later (in the same program run). The object could be any JavaScript type, but I don't need to access it in C++, just store it. Is there a C++ or Qt type that I can use for this, and is there a way of ensuring that JavaScript won't garbage collect it while it is in the C++ domain?
-
I would say
QJSValue
is the correct type. I am not sure about the garbage collection aspect, but I believe it could work out of the box. -
I want to create a JavaScript object in QML, give it to a C++ module for storage, and retrieve it later (in the same program run). The object could be any JavaScript type, but I don't need to access it in C++, just store it. Is there a C++ or Qt type that I can use for this, and is there a way of ensuring that JavaScript won't garbage collect it while it is in the C++ domain?
-
I would say
QJSValue
is the correct type. I am not sure about the garbage collection aspect, but I believe it could work out of the box. -
@pderocco You could pass it as a string using
JSON.stringify()
andJSON.parse()
on the QML side. -
wrote on 4 Sept 2019, 20:48 last edited by
In case of simple objects, like map, you also can try
QVariantMap
, it can be transparently passed bettween C++ and QML without any convertations. -
I wouldn't be so sure about the no conversion part of passing a QVariantMap between C++ and QML.
In pderocco's case
QJSValue
is the way to go. -
wrote on 5 Sept 2019, 09:20 last edited by IntruderExcluder 9 May 2019, 09:20
I just want to say, that there is ability out of box, without any types registration, to pass JS Arrays/Objects between QML and C++.
QJSValue
can be useful if you are trying to pass some JS classes, which can contains methods, but since it is not an QObject you cannot change ownership of such object. I'm not 100% sure, but it looks like that after garbage collection this object can point to null value, so you should be carefull with this. -
I just want to say, that there is ability out of box, without any types registration, to pass JS Arrays/Objects between QML and C++.
QJSValue
can be useful if you are trying to pass some JS classes, which can contains methods, but since it is not an QObject you cannot change ownership of such object. I'm not 100% sure, but it looks like that after garbage collection this object can point to null value, so you should be carefull with this.wrote on 7 Sept 2019, 00:17 last edited by@intruderexcluder Well, I did a simple test. I created a dummy QML app, adding code to main.cpp to register a JSValue type:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "JSValue.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); qmlRegisterType<JSValue>("JSValue", 1, 0, "JSValue"); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
The JSValue is defined in a simple header, JSValue.h:
#include <QJSValue> #include <QObject> class JSValue: public QObject { Q_OBJECT QJSValue value; public: JSValue(QObject* p = nullptr): QObject(p) {} Q_INVOKABLE void set(QJSValue v) { value = v; } Q_INVOKABLE QJSValue get() { return value; } };
And the app is defined in main.qml:
import QtQuick 2.13 import QtQuick.Window 2.13 import JSValue 1.0 Window { visible: true width: 400 height: 400 title: "QJSValueTest" JSValue { id: jsvalue } Component.onCompleted: { var v = function(x) { return x * x } jsvalue.set(v) v = null console.log(jsvalue.get()(2)) gc() console.log(jsvalue.get()(2)) } }
This creates a function closure, gives it to the JSValue object, clears the JavaScript variable that refers to it, fetches it back from the JSValue object and calls it, then runs the garbage collector, and does it again. It produces the correct result before and after calling gc(). So either QJSValue objects are automatically registered as reachable from JavaScript's root object (which would seem sensible to me), or there is some other reference to the function closure that I don't see, or gc() doesn't necessarily do a full garbage collection synchronously when asked.
Does this look like a good test?
-
wrote on 7 Sept 2019, 06:17 last edited by
I decided to ask this question specifically in a separate post, just in case a JS memory manager expert might not have found the title of this post interesting. Here's the other post:
https://forum.qt.io/topic/106655/does-qjsvalue-protect-its-value-from-garbage-collection
1/10