0

I implemented SharedGadget for working with std::shared_ptr in QML:

class SharedGadget
{
    Q_GADGET

    Q_PROPERTY(QObject* p READ pointer CONSTANT);
    Q_PROPERTY(bool empty READ empty CONSTANT);
    Q_PROPERTY(int useCount READ useCount);

public:

    SharedGadget() = default;

    SharedGadget(std::shared_ptr<QObject> p) : m_p(std::move(p)) {}

    template <class T> requires std::is_base_of_v<QObject, T>
    SharedGadget(std::shared_ptr<T> p_t) : SharedGadget(std::static_pointer_cast<QObject>(p_t)) {}

    SharedGadget(const SharedGadget& other) = default;
    SharedGadget(SharedGadget&& other) = default;

    ...
private:

    QObject* pointer() const
    {
        return m_p.get();
    }

    bool empty() const
    {
        return m_p == nullptr;
    }

    int useCount() const
    {
        return m_p.use_count();
    }

    std::shared_ptr<QObject> m_p;
};

see the full source code on GitHub. I actually did what was recommended here, except that I made it not Q_OBJECT but Q_GADGET that probably produces more efficient code.

My SharedGadget can be used like this:

class Example : public QObject
{
    Q_OBJECT
    QML_ELEMENT

public:

    Q_INVOKABLE qtil::SharedGadget createObject()
    {
        return std::make_shared<SomeObject>;
    }
};

Where SomeObject is a class derived from QObject.

That all worked nicely until I tried to use StackView.push in QML:

property var bot

function openBotPageViaPush()
{
    var url = Qt.resolvedUrl("BotPage.qml")

    console.log("url: ", url)

    if (url)
    {
        console.log("Creating BotPage...")

        mainStack.push(url, {"bot1" : bot, "bot2" : bot, "bot3" : bot})
    }
}

see the code on GitHub. bot is a property containing my SharedObject and bot1, bot2 and bot3 are the properties of BotPage.

Surprisingly, this code does not increase useCount of bot and useCount remains 1 which means that SharedObject copy constructor is not called in C++.

But if I create BotPage in this way:

function openBotPageViaComponent()
{
    dynamicComp = Qt.createComponent("BotPage.qml")
    dynamicDlg = dynamicComp.createObject(mainWindow, {"bot1" : bot, "bot2" : bot, "bot3" : bot});
    mainStack.push(dynamicDlg)
}

useCount is increased.

What is the difference?

To reproduce this effect clone the repository https://github.com/dmitriano/GcBug, build it with CMake, run the app and when you press "Open Page Via Comp" button you will see

qml: BotPage has been created.
qml: {"p":{"objectName":"gcbug::BotModel","type":"gcbug::BotModel","name":"Page Comp"},"empty":false,"useCount":7}
qml: {"p":{"objectName":"gcbug::BotModel","type":"gcbug::BotModel","name":"Page Comp"},"empty":false,"useCount":7}
qml: {"p":{"objectName":"gcbug::BotModel","type":"gcbug::BotModel","name":"Page Comp"},"empty":false,"useCount":7}

in the console output, but when you press "Open Page Via Push" you will see

qml: BotPage has been created.
qml: {"p":{"objectName":"gcbug::BotModel","type":"gcbug::BotModel","name":"Page Push"},"empty":false,"useCount":1}
qml: {"p":{"objectName":"gcbug::BotModel","type":"gcbug::BotModel","name":"Page Push"},"empty":false,"useCount":1}
qml: {"p":{"objectName":"gcbug::BotModel","type":"gcbug::BotModel","name":"Page Push"},"empty":false,"useCount":1}

Tested this with QT6.4 and MSVC2022.

Dmitriano
  • 1,878
  • 13
  • 29

0 Answers0