2011-02-16 8 views
5

Tengo un programa que usa QtScript para algunas automatizaciones. He añadido un montón de funciones y clases de C++ para el alcance global del motor de scripts para que los scripts pueden acceder a ellos, así:Copia en profundidad de QScriptValue como objeto global

QScriptValue fun = engine->newFunction(systemFunc); 
engine->globalObject().setProperty("system", fun); 

me gustaría ser capaz de ejecutar varias secuencias de comandos en la serie, cada uno con un nuevo estado global. Así que si uno secuencia de comandos establece una variable global, como

myGlobalVar = "stuff"; 

quiero esa variable a ser borrado antes de que acabe la siguiente secuencia de comandos. Mi método para hacer esto es hacer una copia profunda del Objeto Global del motor de script, y luego restaurarlo cuando un script termina de ejecutarse. Pero las copias profundas no están funcionando, ya que mi función system rompe repentinamente con el error:

TypeError: Result of expression 'system' [[object Object]] is not a function. 

Aquí está mi función de copia profunda, adaptado de:
http://qt.gitorious.org/qt-labs/scxml/blobs/master/src/qscxml.cpp

QScriptValue copyObject(const QScriptValue& obj, QString level = "") 
{ 
    if(obj.isObject() || obj.isArray()) { 
     QScriptValue copy = obj.isArray() ? obj.engine()->newArray() : obj.engine()->newObject(); 
     copy.setData(obj.data()); 
     QScriptValueIterator it(obj); 
     while(it.hasNext()) { 
      it.next(); 
      qDebug() << "copying" + level + "." + it.name(); 
      if(it.flags() & QScriptValue::SkipInEnumeration) 
       continue; 
      copy.setProperty(it.name(), copyObject(it.value(), level + "." + it.name())); 
     } 
     return copy; 
    } 

    return obj; 
} 

(el SkipInEnumeration se poner en para evitar un bucle infinito)

EDITAR: Parte del problema, creo, es que en el depurador (QScriptEngineDebugger), se supone que las funciones y constructores que he agregado para que aparezca como tipo Function, pero después de copiar aparecen como tipo Object. Todavía no he encontrado una buena manera de crear una nueva Función que duplique una existente (QScriptEngine :: newFunction toma un puntero de función real).

Respuesta

1

Lo tengo trabajando. Aquí está la solución en caso de que sea útil para cualquier otra persona:

QScriptValue copyObject(const QScriptValue& obj) 
{ 
    if((obj.isObject() || obj.isArray()) && !obj.isFunction()) { 
     QScriptValue copy = obj.isArray() ? obj.engine()->newArray() : obj.engine()->newObject(); 
     copy.setData(obj.data()); 
     QScriptValueIterator it(obj); 
     while(it.hasNext()) { 
      it.next(); 
      copy.setProperty(it.name(), copyObject(it.value())); 
     } 
     return copy; 
    } 

    return obj; 
} 

La parte importante es la adición del cheque !obj.isFunction(), que acaba de copiar funciones como son, y no hace una copia profunda. La sutileza aquí es que isObject() devolverá verdadero si el elemento es una Función, que no queremos. Esto está documentado en los documentos de Qt y me encontré con esto hace unos momentos.

Además, esta comprobación eliminó la necesidad de evitar copiar elementos marcados SkipInEnumeration. El ciclo infinito se corrige al buscar funciones y copiarlas tal como están. Salir en el SkipInEnumeration realmente rompió algunas otras cosas, como la función eval y un montón de otros complementos.

2

Con el fin de hacer multi-threading disponible en QtScript, necesitaba una forma de copiar profundamente objetos QScriptValue a otro QScriptEngine y tropecé con esta pregunta. Desafortunadamente, el código de Dave no era suficiente para esta tarea, y tiene algunos problemas incluso cuando se copia dentro de un solo QScriptEngine. Entonces necesitaba una versión más sofisticada. Estos son los problemas que tuve que abordar en mi solución:

  1. El código de Dave da como resultado un desbordamiento de la pila cuando un objeto contiene una referencia a sí mismo.
  2. Quería que mi solución respetara las referencias a los objetos para que las referencias múltiples a un objeto no causen que el objeto al que se hace referencia se copie más de una vez.
  3. Como los objetos profundamente copiados QScriptValue se usan en un QScriptEngine diferente de sus objetos fuente, necesitaba una forma de copiar realmente, p. Ej. funciones también.

Podría ser útil para otra persona, así que aquí está el código que se me ocurrió:

class ScriptCopier 
{ 
public: 
    ScriptCopier(QScriptEngine& toEngine) 
     : m_toEngine(toEngine) {} 

    QScriptValue copy(const QScriptValue& obj); 

    QScriptEngine& m_toEngine; 
    QMap<quint64, QScriptValue> copiedObjs; 
}; 


QScriptValue ScriptCopier::copy(const QScriptValue& obj) 
{ 
    QScriptEngine& engine = m_toEngine; 

    if (obj.isUndefined()) { 
     return QScriptValue(QScriptValue::UndefinedValue); 
    } 
    if (obj.isNull()) { 
     return QScriptValue(QScriptValue::NullValue); 
    } 

    // If we've already copied this object, don't copy it again. 
    QScriptValue copy; 
    if (obj.isObject()) 
    { 
     if (copiedObjs.contains(obj.objectId())) 
     { 
      return copiedObjs.value(obj.objectId()); 
     } 
     copiedObjs.insert(obj.objectId(), copy); 
    } 

    if (obj.isQObject()) 
    { 
     copy = engine.newQObject(copy, obj.toQObject()); 
     copy.setPrototype(this->copy(obj.prototype())); 
    } 
    else if (obj.isQMetaObject()) 
    { 
     copy = engine.newQMetaObject(obj.toQMetaObject()); 
    } 
    else if (obj.isFunction()) 
    { 
     // Calling .toString() on a pure JS function returns 
     // the function's source code. 
     // On a native function however toString() returns 
     // something like "function() { [native code] }". 
     // That's why we do a syntax check on the code. 

     QString code = obj.toString(); 
     auto syntaxCheck = engine.checkSyntax(code); 

     if (syntaxCheck.state() == syntaxCheck.Valid) 
     { 
      copy = engine.evaluate(QString() + "(" + code + ")"); 
     } 
     else if (code.contains("[native code]")) 
     { 
      copy.setData(obj.data()); 
     } 
     else 
     { 
      // Do error handling… 
     } 

    } 
    else if (obj.isVariant()) 
    { 
     QVariant var = obj.toVariant(); 
     copy = engine.newVariant(copy, obj.toVariant()); 
    } 
    else if (obj.isObject() || obj.isArray()) 
    { 
     if (obj.isObject()) { 
      if (obj.scriptClass()) { 
       copy = engine.newObject(obj.scriptClass(), this->copy(obj.data())); 
      } else { 
       copy = engine.newObject(); 
      } 
     } else { 
      copy = engine.newArray(); 
     } 
     copy.setPrototype(this->copy(obj.prototype())); 

     QScriptValueIterator it(obj); 
     while (it.hasNext()) 
     { 
      it.next(); 

      const QString& name = it.name(); 
      const QScriptValue& property = it.value(); 

      copy.setProperty(name, this->copy(property)); 
     } 
    } 
    else 
    { 
     // Error handling… 
    } 

    return copy; 
} 

Nota: Este código utiliza el método de Qt-QScriptValue::objectId() interna.

+0

En mi caso 'isVariant()' devuelve falso al menos para los números. Tuve que agregar un caso 'isNumber()' (y todas las otras primitivas para precaución) también. – Olivetree

Cuestiones relacionadas