I am making a project using Q_OBJECT and Q_PROPERTY to access some objects from scripts. I have two problems:
The class B gets the forward declaration to A, because A needs the complete B type in the header due to the templates. B needs only an incomplete type (A*) in the header, thus the forward declaration is valid.
We cannot return a copy, as we need access to the actual object in the script. We cannot return a reference, as Qt does not allow slots to return references - their type would be ignored, they would only return void*.
Complete code download on pastebin or as ZIP archive or as ZIP archive of minimal example is available, for testing / playing: I needed to split up the files for the forward declaration and for MOC. I added a Makefile to test it. Make deps: g++, moc, Qt.
class A; // forward declaration necessary, see explanation above
class B : public QObject {
Q_OBJECT
Q_PROPERTY(A a READ GetA) // <-- ERROR HERE
// ...
public slots:
A* GetA() {
return mA;
}
private:
A* mA;
// ...
}
The error line in the script:
print(bObj.GetA().GetName());
This error disappears when I comment out the Q_PROPERTY above.
tmp/B.moc.hpp:95:51: error: invalid use of incomplete type ‘struct A’
tmp/../B.hpp:10:7: error: forward declaration of ‘struct A’
When leaving out the Q_PROPERTY and calling the GetA() method as a slot from the script, I get the following exception:
Line 7: "TypeError: cannot call GetA(): unknown return type `A*'
(register the type with qScriptRegisterMetaType())"
When registering A* with qRegisterMetaType<A*>("A*");
this changes to:
Line 7: "TypeError: Result of expression 'bObj.GetA().GetName'
[undefined] is not a function."
That shows that GetA() does not return the A object, or somehow it returns a pointer, but the script cannot dereference it. GetA() then actually returns a QVariant(A*)
, can this be used somehow?
operator.
, if something similar exists) or
A
, not A*
, that's why you get very reasonable error.You should use QScriptValue
. Look this code. It works Ok:
class A; // forward declaration necessary, see explanation above
class B : public QObject
{
Q_OBJECT
// Using QScriptValue, made from A instead of A to allow script work correctly with an object
Q_PROPERTY(QScriptValue a READ GetA)
public slots:
QScriptValue GetA() {
//making QScriptValue from A. Type conversion in C style only due to limitation of incomplete type
//In real app it's beter to put defenition of this slot after A's defenition
return static_cast<QScriptEngine*>(parent())->newQObject((QObject*)mA);
}
private:
A* mA;
// ...
public:
//I decided my object will be a child of scriptEngine, but you can take a pointer to it in some other way
B(QScriptEngine * parent);
};
class A: public QObject
{
Q_OBJECT
public slots:
QString GetName() const {return "a name";}
public:
A(QScriptEngine*parent):QObject(parent){}
};
B::B(QScriptEngine *parent):QObject(parent), mA(new A(parent)){}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With