Accessing class member variable inside a lambda causes crash - c++

I have a simple class that looks like this:
class QNetworkAccessManager;
class FontiDownloader : public QObject
{
Q_OBJECT
public:
FontiDownloader(QNetworkAccessManager* netManager);
void downloadFonti();
private:
QString m_dataLocation;
};
The implementation of downloadFonti() is something like the following:
// the networkrequest is just a wrapper that makes a QNetworkRequest
auto nr = new NetworkRequest(m_netManager, this);
connect(nr, &NetworkRequest::completed, [this](QNetworkReply* reply){
// crash occurs here
QString dataLocation = m_dataLocation;
});
nr->send(QUrl(FONTI_URL), HttpMethod::GET);
When I call my lambda function inside downloadFonti(), the program crashes and I have located the reason to be the line:
QString dataLocation = m_dataLocation;
I can see using the debugger that at the time of calling that line, m_dataLocation is "not accessible". However, I cannot think why this would be the case.

Related

Changing members of Q_GADGET from QML

I am trying to propagate Q_GADGET as a Q_PROPERTY into QML, change it there and pass it back into C++.
I have class that derives from Q_OBJECT, which has the Q_GADGET class as a member.
class Foo : public QObject
{
Q_OBJECT
Q_PROPERTY(QGadgetClass bar READ bar WRITE setBar NOTIFY barChanged)
public:
...
QGadgetClass bar() const { return bar_; }
void setBar(const QGadgetClass &bar) { bar_ = bar; emit barChanged(); }
...
signals:
void barChanged();
private:
QGadgetClass bar_;
}
The Q_GADGET class looks like this:
class QGadgetClass
{
Q_GADGET
Q_PROPERTY(AnotherQGadgetClass test READ test WRITE setTest)
... // there are also properties ID & name
public:
...
// QGadgetClass getMyself() const { return *this; } // function explained later
AnotherQGadgetClass test() const { return test; }
void setTest(const AnotherQGadgetClass &test) { test_ = test; }
...
private:
AnotherQGadgetClass test_;
}
Q_DECLARE_METATYPE(QGadgetClass)
I am trying to access Q_GADGET from QML classic way like accessing a Q_OBJECT, but the setters are not called. If I get AnotherQGadgetClass via getter and change it's properties, the setters are called and everything works, but for some reason I cannot manipulate the QGadgetClass. My code in QML looks like this:
Item {
property var bar: foo.bar
function changeBar()
{
console.log(bar.name) // works
console.log(bar.id) // works
bar.name = "New name" // the WRITE function of Q_PROPERTY(name ...) is not called
console.log(bar.name) // shows old name
console.log(bar.test) // prints out AnotherQGadgetClass correctly
var temp = bar.test // copies AnotherQGadgetClass correctly
console.log(temp.name) // prints AnotherQGadgetClass's name
temp.name = "New temp name" // setter is called
console.log(temp.name) // prints new name
bar.test = temp // constructor is NOT called
console.log(bar.test) // prints out old AnotherQGadgetClass
// following code works and will be explained bellow this code
var aa = bar.getMyself() // calls the "hackish" method
console.log(aa.name) // prints out name of QGadgetClass
aa.name = "New name" // calls the setter
console.log(aa.name) // prints out new name
}
}
I have done some research already, but found nothing but this page. I have also found some very unpretty solution here and it worked, but I find it very hacky.
Note that every Q_GADGET is declared as metatype via Q_DECLARE_METATYPE(...) & is registered before usage via qRegisterMetaType<...>("...").
Is there any prettier solution to access QGadgetClass directly from QML, without need to call getMyself() method? Why are the Q_GADGET class setters not called?
A Q_GADGET is always treated as a value type in QML: it must be passed by copying. So the object that you manipulate in QML is not the same instance that you created in C++, and property changes aren't visible in the original. Many related issues are linked from https://bugreports.qt.io/browse/QTBUG-82443

C++ how to have seperate versions of the same method?

I'm sorry if I don't know the right word for what I'm trying to accomplish.
Basically I have an event handler object which only has a single member. The member is a Stage object.
When the event handler receives an event, I want it to simply use the stage object to call the relevant method. For example:
Event event; //this event is not part of my code, but rather the library I'm using.
Stage s; // my custom class object
EventHandler event_handler; //also my custom class object
event_handler.stage = &s;
if(event == SHUTDOWN) {
event_handler.stage->handle_shutdown();
}
So what I'm trying to accomplish is that, there will be seperate scopes that my program goes into over time, and I want each scope to have access to the event_handler such that they can do something like:
void some_other_scope(EventHandler* eh) {
Stage* some_new_stage = new Stage(...);
eh->stage = some_new_stage;
}
This way, the original event code stays the same, and the event handler will be calling handle_shutdown on a different object than it was originally going to.
So what I want to do is to overload the handle_shutdown method so that there can be different implementations of it. I know how basic overloading works, it can be done by specifying different parameters, but is there any way to have different definitions of the same class method based on the file that the object was created in?
I was hoping to have several files, each with their own some_other_scope() function, and each file can redefine the handle_shutdown method to do different things based on what that file needs.
I'm sure there's a way to do what I want, I just don't know the right words to use.
It seems you want to use polymorphism:
class IStage
{
public:
virtual ~IStage() = default;
virtual void handle_shutdown() = 0;
// ...
};
class Stage1 : public IStage
{
public:
void handle_shutdown() override { /*Implementation1*/ }
// ...
};
class Stage2 : public IStage
{
public:
void handle_shutdown() override { /*Implementation1*/ }
// ...
};
And then
struct EventHandler
{
std::unique_ptr<IStage> stage;
// ...
};
EventHandler event_handler;
event_handler.stage = std::make_unique<Stage1>();
if (event == SHUTDOWN) {
event_handler.stage->handle_shutdown();
}
// Later
event_handler.stage = std::make_unique<Stage2>();
if (event == SHUTDOWN) {
event_handler.stage->handle_shutdown();
}

Passing an object from QML to C++

I am working on a simple app to demonstrate integration of C++ with QML but I have problems. In nutshell I want to create a C++ object in qml on fly and pass it to C++ for processing. Here is my code.
import QtQuick 2.4
import QtQuick.Window 2.2
import test 1.0
Window {
visible: true
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
console.log("clicked")
var student = Qt.createComponent("Student")
student.name = "Hello frome QML";
console.log( student.name ) // good
school.addStudent( student )
}
}
}
Student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
QString m_name;
public:
explicit Student(QObject *parent = 0);
~Student();
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
QString name() const
{
return m_name;
}
signals:
void nameChanged(QString arg);
public slots:
void setName(QString arg)
{
if (m_name == arg)
return;
m_name = arg;
emit nameChanged(arg);
}
};
#endif // STUDENT_H
Now when I addStudent() to the school class, there is where there is trouble. The concern .cpp file below.
#include "school.h"
#include <QDebug>
School::School(QObject *parent) : QObject(parent)
{
}
School::~School()
{
}
// why does this not work? it cause compiler error..can't access private members of QObject
//void School::addStudent(Student student)
//{
//// students.append( &student );
//// qDebug() << "added";
//// qDebug() << student.name();// << " was added to school";
//}
void School::addStudent(Student * student)
{
students.append( student );
qDebug() << "added";
qDebug() << student->name();// if this line is there, it breaks the application
}
The questions are also in comments inside the code but the summaries:
Why does addStudent() crashes when I try to access student.name? I can access it though in the qml file after I set it.
Why does my project not compile if I pass Studentby value like seen in the code?
Additional thoughts
It seems like it may have to do with the C++ object getting destroyed by the time C++ function execute. What is the life cycle of C++ object created in QML? When does it gets destroyed? Could the lifetime of the object be an issue here?
Assume that you register the Student type to QML via qmlRegisterType in cpp file.
In your QML, Qt.createComponent("Student") does not create a Student object but a QQmlComponent. Try to set properties other than name and it still works since the object is not a Student. Instead, you should create an object from a pre-defined component via Component.createObject:
MainForm {
id: mainForm
mouseArea.onClicked: {
var student = studentComponent.createObject(mainForm);
student.name = "Hello frome QML";
school.addStudent( student );
}
Component {
id: studentComponent
Student{}
}
}
And everything works fine now.
Back to your code,
//QML
var student = Qt.createComponent("Student")
student.name = "Hello frome QML";
console.log( student.name ) // good
No, it's not good. student.abcdefg = "Hello from QML" works, too. Add console.log(student) and you can see that student is not a Student.
Why does addStudent() crashes when I try to access student.name? I can access it though in the qml file after I set it.
It crashed since the object passed from QML is not a pointer Student. QML engine passes a null pointer to this function. What you accessed in QML is not a Student.
Why does my project not compile if I pass Student by value like seen in the code?
Because Student is an QObject, which is not copyable. Pass by pointer instead.
What is the life cycle of C++ object created in QML? When does it gets destroyed? Could the lifetime of the object be an issue here?
The lifetime is not a issue in your QML since what you created is a component, not an object. When creating Student object in this answer,
var student = studentComponent.createObject(mainForm);
the newly created object sets mainForm as it's parent. The object won't be deleted until mainForm is released. Except you delete it explicitly.
Try moving the declaration of the function QString name() const in the declaration of class Student to before the Q_PROPERTY macro. Or specify "public:" again before the name function. The macro is resetting the access specifier to "private".

How to implement a singleton provider for qmlRegisterSingletonType?

I want to use C++ Classes as Singleton instances in QML and figured that I have to register them with qmlRegisterSingletonType. This function requires a function which provides an instance of the registered C++ class. I am using latest Qt 5.3.1 with included MinGW 4.8 on Windows.
The documentation shows the following example of a provider function:
static QJSValue example_qjsvalue_singletontype_provider(QQmlEngine *engine,
QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
static int seedValue = 5;
QJSValue example = scriptEngine->newObject();
example.setProperty("someProperty", seedValue++);
return example;
}
I tried to use this, but I get a compiler warning when I define such a function outside of class scope in a header, occurring in another cpp file including the same header:
warning: 'QObject* example_qjsvalue_singletontype_provider(QQmlEngine*,
QJSEngine*)' defined but not used [-Wunused-function]
Furtheron, it just feels wrong to write a singleton provider which returns a new instance when called from different cpp files. So I tried an own implementation where I use a static class member to return the instance:
// mysingleton.h
class MySingleton: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(MySingleton)
public:
static QObject *qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
if(!m_instance)
{
m_instance = new MySingleton();
}
return m_instance;
}
MySingleton(QObject* parent = 0)
:QObject(parent)
{}
private:
static QObject* m_instance;
};
I tried to register this using ...
qmlRegisterSingletonType<MySingleton>(uri, 1, 0, "MySingleton",
MySingleton::qmlInstance);
This solution doesn't work either, I am getting linker errors:
release/main.o:main.cpp:
(.text$_ZN11MySingleton11qmlInstanceEP10QQmlEngineP9QJSEngine[__ZN11MySingleton11
qmlInstanceEP10QQmlEngineP9QJSEngine]+0x42): undefined reference to
`MySingleton::m_instance'
What is the correct solution to provide the required Singleton instance 1) with a function outside of class scope and 2) with a class member function?
Why does the example suggest to create a new instance on every call of the provider function?
it just feels wrong to write a singleton provider which returns a new instance when called from different cpp files. So I tried an own implementation where I use a static class member to return the instance
Quote from documentation to qmlRegisterSingletonType function:
NOTE: A QObject singleton type instance returned from a singleton type provider is owned by the QML engine. For this reason, the singleton type provider function should not be implemented as a singleton factory.
It means that such behaviour when singleton type provider returns a new instance is done by intention despite the fact that it, as you have noted, looks weird at first glance. So, your class implementation should look something like below:
class MySingleton: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(MySingleton)
MySingleton() {}
public:
static QObject *qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
return new MySingleton;
}
};
Moreover, you should use ampersand for MySingleton::qmlInstance since it is a member method. See this for more info. Then registration should look so:
qmlRegisterSingletonType<MySingleton>(uri, 1, 0, "MySingleton", &MySingleton::qmlInstance);
The problem is (your second example) that you have to initialize your m_instance static member variable. You can do it in mysingleton.cpp file, like:
QObject * MySingleton::m_instance = 0;
In Qt 5.6 the note in the documentation for qmlRegisterSingletonType was updated:
NOTE: A QObject singleton type instance returned from a singleton type
provider is owned by the QML engine unless the object has explicit
QQmlEngine::CppOwnership flag set.
So you can have complete control over how the singleton instance is created.
Here's an example that uses Q_GLOBAL_STATIC:
Q_GLOBAL_STATIC(MySingleton, singletonInstance)
MySingleton::MySingleton(QObject *parent) :
QObject(parent)
{
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
}
MySingleton *MySingleton::instance()
{
return singletonInstance();
}
QObject *MySingleton::qmlInstance(QQmlEngine *, QJSEngine *)
{
return MySingleton::instance();
}
If you want to call this singleton class also from c++ please change the line
return new MySingleton;
to:
return new &MySingleton;

Qt invoking qml function with custom QObject type

I want to invoke a qml method from c++ passing as a parameter a custom type.
This is my custom type (*Note that all the code I post is not exactly what i'm using, i've extracted the relevant parts, so if you try to compile it is possible that some changes have to be made)
Custom.h
class Custom : public QObject
{
Q_OBJECT
Q_PROPERTY(QString ssid READ getSSID WRITE setSSID)
public:
Q_INVOKABLE const QString& getSSID() {
return m_ssid;
}
Q_INVOKABLE void setSSID(const QString& ssid) {
m_ssid = ssid;
}
}
private:
QString m_ssid;
};
Q_DECLARE_METATYPE(NetworkInfo)
Custom.cpp
Custom::Custom(): QObject() {
setSSID("ABC");
}
Custom::Custom(const Custom & other): QObject() {
setSSID(other.getSSID());
}
This is where I call the qml method
void MyClass::myMethod(const Custom &custom) {
QMetaObject::invokeMethod(&m_item,
"qmlMethod", Q_ARG(QVariant, QVariant::fromValue(custom)));
}
where QQuickItem& m_item
I have declared Custom as QML and QT meta types
qmlRegisterType<Custom>("com.mycustom", 1, 0, "Custom");
When i try to access the parameter inside the qml
import com.mycustom 1.0
function(custom) {
console.log(custom.ssid)
}
I get
qml: undefined
If i do console.log(custom) I get
qml: QVariant(Custom)
so, i'd say my type is correctly imported into Qt (I have used it without problems instantiating it directly in Qml with
Custom {
....
}
Now, the question :)
Why can't I access the ssid property ?
Since you seem to have registered everything correctly, you might be encounting a bug that sometimes happen with QML where objects seem to be stuck in a QVariant wrapping. Try the following and see if it work.
import com.mycustom 1.0
property QtObject temp
function(custom) {
temp = custom;
console.log(temp.ssid);
}
Sometimes forcing your instance to be a QtObject will remove the annoying "QVariant wrapper" and solve the issue.