Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Pass opaque JavaScript type to and from C++?
QtWS25 Last Chance

Pass opaque JavaScript type to and from C++?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
qmljavascriptc++
10 Posts 4 Posters 1.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    pderocco
    wrote on 3 Sept 2019, 23:28 last edited by
    #1

    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?

    T 1 Reply Last reply 4 Sept 2019, 17:05
    0
    • G Offline
      G Offline
      GrecKo
      Qt Champions 2018
      wrote on 4 Sept 2019, 07:44 last edited by
      #2

      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.

      P 1 Reply Last reply 4 Sept 2019, 18:01
      1
      • P pderocco
        3 Sept 2019, 23:28

        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?

        T Offline
        T Offline
        Tom_H
        wrote on 4 Sept 2019, 17:05 last edited by
        #3

        @pderocco You could pass it as a string using JSON.stringify() and JSON.parse() on the QML side.

        P 1 Reply Last reply 4 Sept 2019, 18:03
        1
        • G GrecKo
          4 Sept 2019, 07:44

          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.

          P Offline
          P Offline
          pderocco
          wrote on 4 Sept 2019, 18:01 last edited by
          #4

          @grecko That sure looks like the ticket. It'll take a while to integrate that into my app, but I'll report back here whether it works okay or not. Thanks for that pointer.

          1 Reply Last reply
          0
          • T Tom_H
            4 Sept 2019, 17:05

            @pderocco You could pass it as a string using JSON.stringify() and JSON.parse() on the QML side.

            P Offline
            P Offline
            pderocco
            wrote on 4 Sept 2019, 18:03 last edited by
            #5

            @tom_h That would work for simple objects, but the main thing I want to pass is function closures, so that I can set them up to be called in the future with values from the present.

            1 Reply Last reply
            0
            • I Offline
              I Offline
              IntruderExcluder
              wrote on 4 Sept 2019, 20:48 last edited by
              #6

              In case of simple objects, like map, you also can try QVariantMap, it can be transparently passed bettween C++ and QML without any convertations.

              1 Reply Last reply
              0
              • G Offline
                G Offline
                GrecKo
                Qt Champions 2018
                wrote on 5 Sept 2019, 07:37 last edited by
                #7

                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.

                1 Reply Last reply
                0
                • I Offline
                  I Offline
                  IntruderExcluder
                  wrote on 5 Sept 2019, 09:20 last edited by IntruderExcluder 9 May 2019, 09:20
                  #8

                  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.

                  P 1 Reply Last reply 7 Sept 2019, 00:17
                  0
                  • I IntruderExcluder
                    5 Sept 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.

                    P Offline
                    P Offline
                    pderocco
                    wrote on 7 Sept 2019, 00:17 last edited by
                    #9

                    @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?

                    1 Reply Last reply
                    0
                    • P Offline
                      P Offline
                      pderocco
                      wrote on 7 Sept 2019, 06:17 last edited by
                      #10

                      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 Reply Last reply
                      0

                      1/10

                      3 Sept 2019, 23:28

                      • Login

                      • Login or register to search.
                      1 out of 10
                      • First post
                        1/10
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved