c++ - Instanceof QtScript con clase personalizada arroja error relacionado con el prototipo
ecma262 (1)
Necesitas definir tus prototipos. Esto se complica bastante rápido, pero http://doc.qt.io/archives/qt-4.7/scripting.html#making-use-of-prototype-based-inheritance es una buena referencia de cómo hacerlo.
Tengo un proyecto Qt que usa el módulo QtScript para hacer que algunos componentes de mi aplicación sean programables.
Después de varios intentos de hacer que las clases existentes se puedan usar directamente en QtScript, elegí ir a las clases contenedoras que heredan QObject y QScriptable (principalmente porque tengo algunas clases derivadas que no son qobject que son heredadas por otros que heredan QObject o no y fue por lo tanto, es imposible para mí tratar todas mis clases de manera uniforme).
Ahora estoy tratando de usar una herencia basada en prototipos.
Tengo clases Drawable
y MeshDrawable
que tienen envoltorios correspondientes Wrapper_Drawable
y Wrapper_MeshDrawable
. MeshDrawable
hereda Drawable
y Wrapper_MeshDrawable
hereda Wrapper_Drawable
.
Hago que ambas clases sean conocidas por el motor de scripts ( 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);
Si no me equivoco, el documento dice que el motor de script aplicará el prototipo de Wrapper_Drawable
a objetos de tipo Wrapper_MeshDrawable
ya que tienen una relación de herencia en C ++.
Tengo un método Wrapper_Drawable::drawables()
que devuelve todos los elementos Wrapper_Drawable
de un Wrapper_Drawable
(en realidad los elementos secundarios del Drawable empaquetados en Wrapper_Drawable
). Como Drawable
es una clase abstracta, todos los niños son realmente MeshDrawable
s.
Como quiero que el usuario "crea" que usa Drawable
s y no Wrapper_Drawable
s, he declarado:
m_scriptEngine->globalObject().setProperty("Drawable", wrapper_drawable_obj);
donde wrapper_drawable_obj fue declarado arriba.
Quería probar si el motor reconoce Wrapper_MeshDrawable
s incluso si están declarados en una matriz de Wrapper_Drawable
s.
curChildren = myDrawable.drawables()[0];
print(curChildren instanceof Drawable);
Lamentablemente, instanceof arroja este error:
TypeError: instanceof called on an object with an invalid prototype property.
si trato de hacer curChildren instanceof Number
, devuelve false, así que supongo que el problema está relacionado con Wrapper_Drawable pero no puedo entender qué es exactamente.
Gracias de antemano por su amable ayuda.
EDIT> He agregado el código de ScriptManager (maneja el motor de script y declara los distintos tipos), Wrapper_Drawable (solo algunos métodos, de lo contrario no puedo publicar este mensaje) y Wrapper_MeshDrawable.
También he verificado dos veces esa instanceof
en un objeto Wrapper_Drawable, devuelve falso con Number, true con Object y arroja el error mencionado anteriormente con Drawable (que es, a diferencia del nombre, un objeto Wrapper_Drawable).
Entonces, está el código de la clase Wrapper_Drawable:
#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() {
}
Y finalmente ScriptManager (donde declaro los diversos tipos al motor de scripts):
#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);
}