QtScript instanceof with custom class throws prototype-related error

1.4k Views Asked by At

I have a Qt project which uses the QtScript module to make some components of my application scriptable.

After several attempts at making the existing classes directly usable in QtScript, I chose to go with wrapper classes which inherit QObject and QScriptable (mainly because I have some non-qobject derived classes which are inherited by others which either inherit QObject or not and it was thus impossible for me to treat all my classes uniformly).

I'm now trying to use prototype-based inheritance.

I have to classes Drawable and MeshDrawable which have corresponding wrappers Wrapper_Drawable and Wrapper_MeshDrawable. MeshDrawable inherits Drawable and Wrapper_MeshDrawable inherits Wrapper_Drawable.

I make both classes known to the script engine (m_scriptEngine):

Wrapper_Drawable* wrapper_drawable = new Wrapper_Drawable();
QScriptValue wrapper_drawable_obj = m_scriptEngine->newQObject(wrapper_drawable);
m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_Drawable*>(),
                                    wrapper_drawable_obj);

Wrapper_MeshDrawable* wrapper_meshDrawable = new Wrapper_MeshDrawable();
QScriptValue wrapper_meshDrawable_obj = m_scriptEngine->newQObject(wrapper_meshDrawable);
m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_MeshDrawable*>(),
                                    wrapper_meshDrawable_obj);

If I'm not mistaken, the doc says that the script engine will apply the prototype of Wrapper_Drawable to objects of type Wrapper_MeshDrawable as they have an inheritance relationship in C++.

I have a method Wrapper_Drawable::drawables() which returns all the children of a the Wrapper_Drawable (actually the children of the Drawable wrapped in the Wrapper_Drawable). As Drawable is an abstract class, all children are actually MeshDrawables.

As I want the user to "believe" that he uses Drawables and not Wrapper_Drawables, I've declared:

m_scriptEngine->globalObject().setProperty("Drawable", wrapper_drawable_obj);

where wrapper_drawable_obj was declared above.


I wanted to test if the engine recognizes Wrapper_MeshDrawables even if they're declared in an array of Wrapper_Drawables.

curChildren = myDrawable.drawables()[0];
print(curChildren instanceof Drawable);

Sadly, instanceof throws this error:

TypeError: instanceof called on an object with an invalid prototype property.

if I try to do curChildren instanceof Number it returns false so I guess the problem is related to Wrapper_Drawable but I cannot figure out what it exactly is.

Thank you in advance for your kind help.

EDIT > I've added the code of ScriptManager (handles the script engine and declares the various types), Wrapper_Drawable (only some methods, otherwise I cannot post this message) and Wrapper_MeshDrawable.

I've also double-checked that instanceof on a Wrapper_Drawable object, returns false with Number, true with Object and throws the above-mentionned error with Drawable (which is, unlike the name says, a Wrapper_Drawable object).

So, there's the Wrapper_Drawable class' code:

#ifndef WRAPPER_DRAWABLE_H
#define WRAPPER_DRAWABLE_H

#include <QObject>
#include <QScriptable>

#include "../drawable.h"

class Wrapper_Drawable : public QObject, public QScriptable
{
    Q_OBJECT
public:
    explicit Wrapper_Drawable(Drawable* drawable = 0, QObject *parent = 0);

    virtual Drawable* drawable() const;
signals:

public slots:
    QScriptValue visible() const;

    virtual QScriptValue loadData();

    QScriptValue rotate();
    QScriptValue translate();
    QScriptValue scale();

    QScriptValue modelMatrix() const;
    QScriptValue completeModelMatrix() const;

    QScriptValue name() const;
    QScriptValue setName();

    QScriptValue shaderProgramName() const;
    QScriptValue setShaderProgramName();

    QScriptValue row() const;

        QScriptValue childCount() const;

    QScriptValue child() const;
    QScriptValue appendChild();
    QScriptValue insertChildren();
    QScriptValue insertChild();
    QScriptValue removeChildren();

    QScriptValue children() const;
    QScriptValue visibleChildren() const;

    QScriptValue parent() const;
    QScriptValue setParent();

protected:
    Drawable* m_drawable;
};

Q_DECLARE_METATYPE(QList<Wrapper_Drawable*>)
Q_DECLARE_METATYPE(Wrapper_Drawable*)

#endif // WRAPPER_DRAWABLE_H

CPP:

#include "wrapper_drawable.h"

Wrapper_Drawable::Wrapper_Drawable(Drawable *drawable, QObject *parent) :
    QObject(parent), m_drawable(drawable)
{
}

Drawable* Wrapper_Drawable::drawable() const {
    return m_drawable;
}

QScriptValue Wrapper_Drawable::removeChildren() {
    Wrapper_Drawable* wrapper_drawable = qscriptvalue_cast<Wrapper_Drawable*>(context()->thisObject());

    if(!wrapper_drawable)
        return context()->throwError(QScriptContext::TypeError, "Drawable.insertChildren: this object is not a Drawable");

    Drawable* drawable = wrapper_drawable->drawable();

    if(!drawable)
        return context()->throwError(QScriptContext::TypeError, "Drawable.insertChildren: no Drawable wrapped");

    if(argumentCount() != 1)
        return context()->throwError(QScriptContext::SyntaxError, "Drawable.insertChildren takes exactly 1 argument");

    // TODO: maybe allow multiple arguments

    if(!argument(0).isNumber())
        return context()->throwError(QScriptContext::TypeError, "Drawable.insertChildren: argument 0 should be a number");

    unsigned int position = argument(0).toNumber();

    if(!argument(1).isNumber())
        return context()->throwError(QScriptContext::TypeError, "Drawable.insertChildren: argument 1 should be a number");

    unsigned int count = argument(1).toNumber();

    return engine()->toScriptValue(drawable->removeChildren(position, count));
}

QScriptValue Wrapper_Drawable::visibleChildren() const {
    Wrapper_Drawable* wrapper_drawable = qscriptvalue_cast<Wrapper_Drawable*>(context()->thisObject());

    if(!wrapper_drawable)
        return context()->throwError(QScriptContext::TypeError, "Drawable.children: this object is not a Drawable");

    Drawable* drawable = wrapper_drawable->drawable();

    if(!drawable)
        return context()->throwError(QScriptContext::TypeError, "Drawable.children: no Drawable wrapped");

    if(argumentCount() > 0)
        return context()->throwError(QScriptContext::SyntaxError, "Drawable.children does not take any argument");

    QList<Drawable*> drawables = drawable->visibleChildren();
    QList<Wrapper_Drawable*> wrapper_drawables;
    for(QList<Drawable*>::const_iterator it = drawables.constBegin(), end = drawables.constEnd();
        it != end; ++it)
    {
        wrapper_drawables.append(new Wrapper_Drawable(*it));
    }

    return engine()->toScriptValue(wrapper_drawables);
}

MeshDrawable:

#ifndef WRAPPER_MESHDRAWABLE_H
#define WRAPPER_MESHDRAWABLE_H

#include "wrapper_drawable.h"

#include "../meshdrawable.h"

class Wrapper_MeshDrawable : public Wrapper_Drawable
{
    Q_OBJECT
public:
    Wrapper_MeshDrawable(MeshDrawable* meshDrawable = 0, QObject *parent = 0);

    virtual MeshDrawable* drawable() const;

public slots:
    QScriptValue addTri();
    QScriptValue addQuad();
    QScriptValue setSmoothing();
};

Q_DECLARE_METATYPE(Wrapper_MeshDrawable*)

#endif // WRAPPER_MESHDRAWABLE_H

CPP:

#include "wrapper_meshdrawable.h"

Wrapper_MeshDrawable::Wrapper_MeshDrawable(MeshDrawable *meshDrawable, QObject *parent) :
    Wrapper_Drawable(meshDrawable, parent)
{
}

MeshDrawable* Wrapper_MeshDrawable::drawable() const {
    return static_cast<MeshDrawable*>(Wrapper_Drawable::drawable());
}

QScriptValue Wrapper_MeshDrawable::addTri() {

}

QScriptValue Wrapper_MeshDrawable::addQuad() {

}

QScriptValue Wrapper_MeshDrawable::setSmoothing() {

}

And finally ScriptManager (where I declare the various types to the script engine):

#ifndef SCRIPTMANAGER_H
#define SCRIPTMANAGER_H

#include <QtScript/QScriptEngine>
#include <QtScriptTools/QScriptEngineDebugger>
#include <QtScriptTools/QtScriptTools>
#include <QStringList>
#include <QObject>

#include "utility.h"

class ScriptManager : public QObject {
    Q_OBJECT
public:
    ScriptManager();

public slots:
    QString interprete(QString command);

private:
    void initializeFunctions();

    QScriptEngine* m_scriptEngine;

    QScriptEngineDebugger* m_scriptEngineDebugger;
};

#endif // SCRIPTMANAGER_H

CPP

#include "scriptmanager.h"
#include "scenegraph.h"

#include "meshdrawable.h"
#include "objdrawable.h"

#include <QScriptValueIterator>

#include "wrappers/wrapper_camera.h"
#include "wrappers/wrapper_cameramanager.h"
#include "wrappers/wrapper_drawable.h"
#include "wrappers/wrapper_meshdrawable.h"
#include "wrappers/wrapper_drawablemanager.h"
#include "wrappers/wrapper_scenegraph.h"
#include "wrappers/wrapper_shadermanager.h"

QString ScriptManager::returnString = QString();

ScriptManager::ScriptManager() : m_scriptEngine(new QScriptEngine())
{
    initializeFunctions();
}

void ScriptManager::initializeFunctions() {
    qScriptRegisterQObjectMetaType<QGLShaderProgram*>(m_scriptEngine);

    qScriptRegisterSequenceMetaType<QList<Wrapper_Drawable*> >(m_scriptEngine);

    QScriptValue function_ls = m_scriptEngine->newFunction(scriptFunction_ls);
    m_scriptEngine->globalObject().setProperty("ls", function_ls);

    QScriptValue function_print = m_scriptEngine->newFunction(scriptFunction_print);
    m_scriptEngine->globalObject().setProperty("print", function_print);

//    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Observer*>(),
//                                        Observer::getDefaultPrototype(m_scriptEngine));

    Wrapper_Drawable* wrapper_drawable = new Wrapper_Drawable();
    QScriptValue wrapper_drawable_obj = m_scriptEngine->newQObject(wrapper_drawable);
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_Drawable*>(),
                                        wrapper_drawable_obj);
    m_scriptEngine->globalObject().setProperty("Drawable", wrapper_drawable_obj);

    Wrapper_MeshDrawable* wrapper_meshDrawable = new Wrapper_MeshDrawable();
    QScriptValue wrapper_meshDrawable_obj = m_scriptEngine->newQObject(wrapper_meshDrawable);
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_MeshDrawable*>(),
                                        wrapper_meshDrawable_obj);
    m_scriptEngine->globalObject().setProperty("MeshDrawable", wrapper_meshDrawable_obj);

    Wrapper_Camera* wrapper_camera = new Wrapper_Camera();
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_Camera*>(),
                                        m_scriptEngine->newQObject(wrapper_camera));

    Wrapper_CameraManager* wrapper_cameraManager = new Wrapper_CameraManager();
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_CameraManager*>(),
                                        m_scriptEngine->newQObject(wrapper_cameraManager));

    Wrapper_DrawableManager* wrapper_drawableManager = new Wrapper_DrawableManager();
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_DrawableManager*>(),
                                        m_scriptEngine->newQObject(wrapper_drawableManager));

    Wrapper_SceneGraph* wrapper_sceneGraph = new Wrapper_SceneGraph(SceneGraph::instance());
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_SceneGraph*>(),
                                        m_scriptEngine->newQObject(wrapper_sceneGraph));

    QScriptValue object_sceneGraph = m_scriptEngine->newQObject("sceneGraph", wrapper_sceneGraph);
    m_scriptEngine->globalObject().setProperty("sceneGraph", object_sceneGraph);

    Wrapper_ShaderManager* wrapper_shaderManager = new Wrapper_ShaderManager();
    m_scriptEngine->setDefaultPrototype(qMetaTypeId<Wrapper_ShaderManager*>(),
                                        m_scriptEngine->newQObject(wrapper_shaderManager));

    m_scriptEngineDebugger = new QScriptEngineDebugger();
    m_scriptEngineDebugger->attachTo(m_scriptEngine);
}
1

There are 1 best solutions below

0
On

You need to define your prototypes. This gets complicated fairly quickly, but http://doc.qt.io/archives/qt-4.7/scripting.html#making-use-of-prototype-based-inheritance is a good reference for how.