How to safely use QQmlEngine::CppOwnership? - c++

When calling C++ from QML, a QObject can be returned to qml by pointer. Before returning, I can call
QObject* qobj = m_sharedPtr.data(); // Pointer to member shared-ptr-managed object.
QQmlEngine::setObjectOwnership(qobj, QQmlEngine::CppOwnership);
return qobj;
but given that QML is garbage-collected, how can this work safely? My mental model is that QML will get the pointer and hold onto it, wrapped in some QML pointer wrapper and that pointer wrapper will eventually be GC'd. But then there's no limit to how long after the setObjectOwnership call that QML could access *qobj. (E.g., perhaps the next QML->C++ call after this one causes m_sharedPtr to go out of scope.) Does that mean QQmlEngine::CppOwnership is only safe to use when the object's lifetime is essentially infinite (e.g., a singleton)? I don't see any alternative, but haven't found any mention of this issue in any documentation.

If m_sharedPtr refers to data created in C++, qobj will have CppOwnership anyways, and you should handle it like any other C++ smart pointer (i.e. let it live as long as you actually need it).
If it was created in QML, it will have JavaScriptOwnership by default. If you then transfer ownership to C++, and it gets destroyed, it's not accessible in QML anymore.
If it is important that qobj is accessible in QML for an uncertain time, but still C++-managed, you could retreat to using raw pointers and handle the destruction yourself (e.g. by connecting to the aboutToClose() signal of a QML window).

Related

Why does Qt use raw pointers?

I have gone back to Qt/C++ programming recently after coding a lot with plain C++.
When browsing StackOverflow, I often catch up on posts like "Why use pointers?" where in most cases the gist of the answers is "if you can avoid it, don't use them".
When coding in C++, I now mostly try using stack variables which are passed by (const) reference or, if necessary, std::shared_ptr resp. std::unique_ptr where needed.
Getting back to Qt, I found all those "principles" to be completely ignored apparently.
I know that Qt uses its own memory management to take care of raw pointers, but here's my question:
Why don't they at least use shared_ptr or unique_ptr, particularly since they even have an own implementation QSharedPointer?
Qt since versions 4.x was designed around imitating Java's framework ideology in C++ environment, using C++98 means. Instead of RAII approach of interaction it establishes "owner" - "slave" relation, in framework's term that's "parent" and "child". More of, Qt uses concept of PIMLP -private implementation. QObjects you operate with aren't real representation of what is happening, they are interfaces to completely hidden inner implementation.
By design, you have to create a QObject-derived object of child element and pass ownership to the owning object . E.g. where a window is an owner, a Button inside window will be the "slave" object. When owner is deleted, all objects that were slaved to it will be deleted too. All QObjects are thread-aware, but QWidgets can work only in main thread. This creates a non-owning pointer:
QWidget *myWidget = new QWidget(mainWindow);
mainWindow will be owning QWidget instance in this case. But this one is owning?
QWidget *myWidget = new QWidget;
It isn't. It's still owned by QApplication.
QObjectDerivedClass *myWidget = new QObjectDerivedClass;
It's an owning pointer, but this object was registered to exist in our framework. Even more, any instance can be found if it was assigned a name, storing QObjects to reach them is just an caching optimization.
All QObjects and QWidgets are registered globally and are iterable. With destruction of QApplicationCore instance all QWidgets of top level will be freed. There is undocumented exception out of that rule at least in Qt 4.x versions that QDesktopWidget objects are ignored even if they are top-level widgets. So, if a QMainWindow was forced to appear on certain screen by becoming its child, it wouldn't be destroyed.
Now comes into play signal-slot connections. In GUI certain handlers begin their work as soon as parent-child connection is established, but you can add new handlers. if a child object is deleted between beginning and end of message pump created by QEventLoop, your program may encounter an UB. To avoid it, you have to call deleteLater() which marks object for deletion at designed moment. Processing signals between threads is done separately. Practically, the main event loop is the only part of GUI that is synced with other threads.
With such complex structure and already existing requirement of working in one thread, imposed by some of supported embedded platforms, need to use smart pointers within GUi framework was negligible compared to possible impact on performance.
Before C++11 Qt had QPointer (and still got it), which is aware if QObject still exists or not using mechanics similar to owner-child interaction.
This design predates C++11 and QSharedPointer appeared only after that to fill a niche requested by users to maintain user-defined data model. It doesn't support some features, e..g you can't have an atomic version of it like ISO version does, it's only partially atomic until very last releases of 5.x. QAtomicPointer isn't either QPointer or QSharedPointer, it acts like a std::atomic<QObject*>. QSharedPointer though allows use of custom non-standalone deleter, including a call of deleteLater():
QSharedPointer<MyDataQObject> objPtr { new MyDataQObject, &QObject::deleteLater };
Assuming that MyDataQObject is derived from QObject, objPtr will call method deleteLater() in context of managed object instead of using delete on managed pointer when destroyed or reset.

How is the lifetime of QObjects returned from C++ to QML managed?

I have a QObject subclass (registered with QML) called ToReturn and a QML singleton, defined in C++, called MySingleton. The latter provides this function:
Q_INVOKABLE ToReturn* get_toReturn() {
return new ToReturn();
}
When I call get_toReturn from QML, am I responsible for later calling destroy() on the returned object? Or is there a way to use automatic memory management for this?
Note that my ToReturn object does not logically belong to an object tree, so I pass nullptr to the parent constructor (QObject's).
It is supposed to work automatically, and objects are supposed to be deleted as soon as they are out of scope, have no parent and no references to them exist.
That being said, there are 2 caveats:
objects will rarely be destroyed as soon as the above conditions are met. They tend to linger for quite a while, unless garbage collection is forced
in more complex scenarios, objects will be deleted, even if they have parents and references to them. It is a critical bug that's been standing for almost 2 years now, unfortunately with zero work done on it. This has forced me to use manual object lifetime management, setting ownership to CPP explicitly just so objects don't go missing, which simply crashes the application
By default the object returned from your function will have QQmlEngine::JavaScriptOwnership unless you explicitly set it otherwise via:
QQmlEngine::setObjectOwnership(objectptr, QQmlEngine::CppOwnership);
EDIT: Note that the premature deletion has to do with JS ownership, so objects that are declared and created entirely in QML are also subject to it. You can set CPP ownership for such object to protect them too, and deletion of such objects can only be done from C++, preferably using deleteLater().

Who owns object returned by QQmlIncubator?

In the following C++ code, a QML component is created using a QQmlIncubator. The Qt documentation has this snippet from here http://doc.qt.io/qt-5/qqmlincubator.html:
QQmlIncubator incubator;
component->create(incubator);
while (!incubator.isReady()) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
}
// Who owns 'object'? When is it deleted?
QObject *object = incubator.object();
It's my understanding that this snippet isn't totally complete, because you'd need to call delete on component. According to http://doc.qt.io/qt-5/qqmlcomponent.html#create, the QQmlComponent::create() function transfers ownership of the returned object instance to the caller. Good so far.
Now comes my question--who owns object in the snippet above? In my case, I've place the above snippet in a member function of a class, so the QQmlIncubator incubator goes out of scope, and I only hold on to component and object, which are both instance variables of their containing class, and I call delete component in the destructor. Am I cleaning up properly?
So does object belong to component? When does object get destroyed?
Update 1
See my follow-up question: Safely deleting QML component being used in StackView transition.
Short answer
You should destruct the incubated object (if the incubator finished successfully), otherwise a memory leakage occur.
How to manage ownership?
A good approach may be to transfer the ownership to an object parent using QObject::setParent, for example to the visual parent. This is also done by Qt when you construct QML objects in the traditional way:
Any object assigned to an item's data property becomes a child of the
item within its QObject hierarchy, for memory management purposes.
For convenience, the Item data property is its default property.
Reasoning
As mentioned in the QQmlComponent::create(QQmlContext *) documentation, the object ownership is transfered to the user:
The ownership of the returned object instance is transferred to the caller.
So, it is unlikely that the overloaded function QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlContext *forContext) will keep the ownership, although it is not mentioned explicitly in the documentation. One could argue that the ownership is transferred to the QQmlIncubator object. This is indeed the case as long as the component is Loading, once it is ready the ownership is released as (implicitly) documented in QQmlIncubator::clear:
Any in-progress incubation is aborted. If the incubator is in the Ready state, the created object is not deleted.
Note that QQmlIncubator::clear is called inside the destructor of QQmlIncubator.
As currently written in your example, object destruction is currently your responsibility. Most likely you will want to take care of that by calling setParent() on object and thus passing on the responsibility to the parent item.
As you point out about component->create():
The ownership of the returned object instance is transferred to the
caller.
and the incubator gives all appearance of preserving that intention.
An experimental way to verify this would be to check the parent of object as returned from the incubator.
Another way to verify this is to look directly at the relevant source code in Qt. Here are the files for QQmlIncubator:
https://github.com/qt/qtdeclarative/blob/dev/src/qml/qml/qqmlincubator.cpp https://github.com/qt/qtdeclarative/blob/dev/src/qml/qml/qqmlincubator_p.h
https://github.com/qt/qtdeclarative/blob/dev/src/qml/qml/qqmlincubator.h
Examining those files, the only place that result is deleted is in the ::clear method:
if (s == Loading) {
Q_ASSERT(d->compilationUnit);
if (d->result) d->result->deleteLater();
d->result = 0;
}

How should I store pointers in Qt?

My pet project has come to the point where I should start tracking pointer lifetime, and I'm trying to develop some system for it. Sadly, the popular advice to use smart pointers everywhere does not apply since Qt API itself uses naked pointers on every occasion. So, what I came up with is this:
For everything owned by Qt,
use pointers naked locally;
pass them between functions naked too;
store them as a subclassed QPointer that makes isNull() check before conversion to naked.
For everything cleanly owned by me, use smart pointers as advised. I'm going to go with the std:: versions here.
The case that bothers me. For objects that switch ownership (like widgets added/removed from a layout)
use, store, pass pointers naked;
delete them manually when appropriate.
Suggestion, comments, advice? I don't like this scheme much myself.
First, hold things by value where you can. View each use of new, make_unique and make_shared with suspicion - you must justify each dynamic object creation. If a sub-object has the same lifetime as the parent, holding by value is a no-brainer. For example:
class MyWidget : public QWidget {
Q_OBJECT
QGridLayout m_topLayout{this};
QLabel m_sign{"Hello World"};
public:
MyWidget(QWidget * parent = nullptr) : QWidget{parent} {
m_topLayout.addWidget(&m_sign, 0, 0);
}
};
You're passing pointers around, but object ownership is clear and there's no change of ownership. Just because a QObject has a parent doesn't mean that the parent "owns" it. If the child is destructed before the parent, the ownership ceases. By using C++ semantics - namely the well-defined order of member construction and destruction - you have full control over child lifetimes and no QObject parent gets to interfere.
If you have non-movable objects that have one owner, use std::unique_ptr and move it around. That's the way to pass dynamically created QObjects around your own code. You can remove them from the pointer at the point where you make their ownership managed by a QObject parent, if there is such.
If you have objects with shared ownership, where their life should end as soon as possible (vs. when the application terminates, or some long-lived object gets destroyed), use std::shared_ptr. Ensure that the pointer outlives the users. For example:
class MyData : public QAbstractItemModel { /* ... */ };
class UserWindow : public QWidget {
Q_OBJECT
std::shared_ptr<MyData> m_data; // guaranteed to outlive the view
QTreeView m_view;
public:
void setData(std::shared_ptr<MyData> && data) {
m_data = std::move(data);
m_view.setModel(m_data.data());
}
};
This example is perhaps contrived, since in Qt most users of objects watch the object's destroyed() signal and react to the objects destruction. But this makes sense if e.g. m_view was a third-party C API object handle that had no way of tracking the data object's lifetime.
If the object's ownership is shared across threads, then the use of std::shared_ptr is essential: the destroyed() signal is only usable within a single thread. By the time you get informed about object deletion in another thread, it's too late: the object has been already destroyed.
Thirdly, when you return instances of dynamically created objects from factory methods, you should return them by a naked pointer: it's clear that a factory creates an object for someone else to manage. If you need exception safety, you can return a std::unique_ptr instead.
For starter, in Rome, be Roman.
QT was developed in the very early 90's and it was a great success at a time.
unfortunately, QT didn't really adopted new features as the time went by so the API itself has very old C++ style to it (and may I say, a Java style to it?)
you can't force QT to suddenly be C++14 because it's not. use the popular-QT conventions when it comes to QT. use raw pointer if that was the platform design goal. use value types when you can't.
But I don't think you make QT work that much with C++14. stick the QT idioms as they given by the platform.

Correct way to manage memory in Qt when exposing QObject class into qml?

I'm using C++ to code the most of my program logic. I like exposing C++ classes into QML so that the QML can use JS to do many complex jobs. However, here comes a problem:
If the C++ function return a QObject*
Q_INVOKABLE QObject* parseJson(const QString& someArguments)
{
return new SomeClassExtendsQObject(someArguments);
}
which is then assigned to a JS variable and used
var result = exposingCppObj.parseJson("I'm Arguments");
result.someMemberFunction(...);
.....
if I use
delete result
in QML JS, is the instance really freed like it would be in C++?
An object created in C++ but accessible from QML can have two ownership states:
QQmlEngine::CppOwnership and QQmlEngine::JavaScriptOwnership. You rarely need to set this state as Qt can usually work out the appropriate one. In your example since the object is created from a QML call, it is automatically assigned QQmlEngine::JavaScriptOwnership.
When an object is assigned QQmlEngine::JavaScriptOwnership the JS garbage collector will destroy it when necessary; there's rarely need to explicitly destroy it.
One last thing, if you do want to destroy an object in QML, use destroy(). The JS delete keyword is for removing both the value of a property and a property itself from objects.