Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt: Q_PROPERTY with pointer and forward declaration for QtScript access

Problem

I am making a project using Q_OBJECT and Q_PROPERTY to access some objects from scripts. I have two problems:

  1. making classes that use forward declarations scriptable
  2. returning a property as pointer

Explanations

1. Why forward declaration?

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.

2. Why returning a pointer?

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*.

Code

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.

Important parts

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());

Compile error

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’

Script exception

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?

Questions:

  1. Can I somehow make a Q_PROPERTY from an incomplete type, or how could I avoid the forward declaration?
  2. Can I return a reference in a slot (maybe some tricks, e.g. a class that wraps the pointer and "overrides" the script operator., if something similar exists) or
  3. Can I somehow dereference a QVariant(A*) to A in QtScript?
like image 990
opatut Avatar asked Aug 05 '11 16:08

opatut


1 Answers

  1. Your property type is A, not A*, that's why you get very reasonable error.
  2. 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)){}
    
like image 194
Lol4t0 Avatar answered Nov 15 '22 17:11

Lol4t0