5

问题

我正在制作一个项目,使用 Q_OBJECT 和 Q_PROPERTY 从脚本中访问一些对象。我有两个问题:

  1. 使使用前向声明的类可编写脚本
  2. 将属性作为指针返回

解释

1. 为什么要前向声明?

类 B 获得对 A 的前向声明,因为由于模板,A 需要头部中的完整 B 类型。B 只需要头部中的不完整类型(A*),因此前向声明是有效的。

2. 为什么要返回一个指针?

我们无法返回副本,因为我们需要访问脚本中的实际对象。我们不能返回引用,因为Qt 不允许槽返回引用- 它们的类型将被忽略,它们只会返回 void*。

代码

可以在 pastebin或 ZIP 存档或最小示例的ZIP 存档上下载完整的代码,用于测试/播放:我需要为前向声明和 MOC 拆分文件。我添加了一个 Makefile 来测试它。制作部门: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;
    // ...
}

脚本中的错误行:

print(bObj.GetA().GetName());

编译错误

当我注释掉上面的 Q_PROPERTY 时,此错误消失。

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’

脚本异常

当省略 Q_PROPERTY 并将 GetA() 方法作为脚本中的插槽调用时,我收到以下异常:

Line 7: "TypeError: cannot call GetA(): unknown return type `A*' 
        (register the type with qScriptRegisterMetaType())"

注册 A* 时,qRegisterMetaType<A*>("A*");此更改为:

Line 7: "TypeError: Result of expression 'bObj.GetA().GetName' 
        [undefined] is not a function." 

这表明 GetA() 不返回 A 对象,或者以某种方式返回指针,但脚本无法取消引用它。GetA() 然后实际上返回 a QVariant(A*),这可以以某种方式使用吗?

问题:

  1. 我可以以某种方式从不完整的类型中创建 Q_PROPERTY,或者如何避免前向声明?
  2. 我可以在插槽中返回一个引用吗(可能是一些技巧,例如一个包装指针并“覆盖”脚本的类operator.,如果存在类似的东西)
  3. 我可以以某种方式将 QVariant(A*) 取消引用到 QtScript 中的 A 吗?
4

1 回答 1

6
  1. 您的属性类型是A, not A*,这就是您得到非常合理的错误的原因。
  2. 你应该使用QScriptValue. 看这段代码。它工作正常:

    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)){}
    
于 2011-08-05T20:43:22.970 回答