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.