[Solved] QtScript (Qt4) Object Ownership Clarification
-
edit: well reading the documentation is hard ... :)
objects created the way the original problem states get AutoOwnership (not QtOwnership as suspected) and get deleted/garbage-collected if they lack a parent (which they do in my case) when the gc runsa solution is do:
@
//Q_SCRIPT_DECLARE_QMETAOBJECT(Material,QObject*)
Q_DECLARE_METATYPE(Material*)// engine setup
Material materialProto;
m_engine->setDefaultPrototype(qMetaTypeId<Material*>(),
m_engine->newQObject(&materialProto));
m_engine->globalObject().setProperty("Material", m_engine->newFunction(CRScriptEngine::newMaterial));QScriptValue CRScriptEngine::newMaterial(QScriptContext *cnt, QScriptEngine eng)
{
Material * foo = new Material();
return eng->toScriptValue((QObject)foo); // casting necessary, so inbuilt QObject conversion is used
}
@Only tiny bit now is, i get memory leaks if the user script does not actually add materials (and other stuff) to the SceneManager (which takes care of freeing memory), but well, can't have everything. Anyway, maybe this soliloquy is still helpful to others :)
----- orignal problem statement -----
Hi everyone,
according to the documentation, objects created by a script default to QtOwnership, which means (to my understanding) they "live" in the C++ world and are fully independent from the ScriptEngine which created them, right?
I'm facing the problem, that my objects are gone (access to them segfaults) after my ScriptEngine Object goes out of scope or is deleted.
Some actual code: (context: using javascript as a scene description language for a ray-/pathtracer)@
class Material : public QObject
{
Q_OBJECTQ_PROPERTY(qreal ambient READ ambient WRITE setAmbient)
public:
qreal m_ambient;
qreal ambient() const;
void setAmbient(qreal ambient);
public slots:
QString toString() const;
// much more properties left out ...
}
Q_SCRIPT_DECLARE_QMETAOBJECT(Material,QObject*) // make custom class known
@in the ScriptEngine setup code, i just do:
@QScriptValue materialClass = m_engine->scriptValueFromQMetaObject<Material>();
m_engine->globalObject().setProperty("Material", materialClass);m_engine->globalObject().setProperty("addObject", m_engine->newFunction(CRScriptEngine::addObject));@
creating the material in the script works fine, so does setting/getting properties.
Now, the material is (via script) added to a SceneManager Singleton which keeps track of all materials, objects etc:user/script does:
@
var m = new Material();
m.ambientColor = [0.1,0.2,0.1]; // ... alot more stuff left out
addObject(s1,m); // object,material
@now for the tricky part... addObject converts the scriptValue to its actual Material object:
@
QScriptValue CRScriptEngine :: addObject(QScriptContext * cnt, QScriptEngine * eng)
{
// snip, check argument count etc
Material * mat = genericCastHelper<Material>(cnt->argument(1), cnt);
SceneManager::instance().addMaterial(mat); // put pointer into some QList<Material *> for later use ...
}// conversion helper template <class T> static T * genericCastHelper( const QScriptValue & scriptVal, QScriptContext * cnt ) { T * obj = 0; obj = dynamic_cast<T*>(scriptVal.toQObject()); if (not obj) { cnt->throwError("cast failed for"+scriptVal.toString()); return 0; } else { return obj; } }
@
After the script is done evaluating, i would like to delete all scripting related things, so the ScriptEngine is deleted or its dtor is called by leaving the scope. Afterwards, during actual rendering, access to e.g. any Material segfaults, which should not happen?
My first idea was that the ScriptEngine is parent to the Material (in terms of QObject Tree) so if its gone, it deletes all children, but the parent() pointer of the material class points at 0x0 (while the engine is still around). Leaving the Engine alive works fine (no segfaults at all) but i would love to avoid this.
The same problem arises for geometric objects (wrapped in a class which inherits QObject and an abstract base class GeometricObject), in this case the pointer the abstract base class remains intact, but when accessing it after deleting the ScriptEngine i get an error like 'calling pure virtual method' because the actual child object (e.g. a sphere) is gone.Any hints what i'm missing?
edit: I know it could be solved by just adding deep copies of everything, but i think that is a very inelegant solution, why should i copy something thats already there?