我有一个使用QtScript进行自动化的程序。我已经在脚本引擎的全局范围中添加了一堆C ++函数和类,以便脚本可以访问它们,如下所示:
QScriptValue fun = engine->newFunction( systemFunc );
engine->globalObject().setProperty( "system", fun );
我希望能够连续运行多个脚本,每个脚本都有一个全新的状态。因此,如果一个脚本设置了一个全局变量,比如
myGlobalVar = "stuff";
我希望在下一个脚本运行之前擦除该变量。我这样做的方法是制作脚本引擎的全局对象的深层副本,然后在脚本完成运行时恢复它。但是深拷贝不起作用,因为我的system
函数突然出现错误:
TypeError: Result of expression 'system' [[object Object]] is not a function.
这是我的深层复制功能,改编自:
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;
}
(放入SkipInEnumeration
以避免无限循环)
编辑:我认为问题的一部分是,在调试器(QScriptEngineDebugger)中,我添加的函数和构造函数应该显示为类型Function
,但在复制后,它们显示为类型Object
。我还没有找到一种创建一个复制现有函数的新函数的好方法(QScriptEngine :: newFunction接受一个实际的函数指针)。
答案 0 :(得分:2)
为了在QtScript中提供多线程,我需要一种方法将QScriptValue
个对象深层复制到另一个QScriptEngine
并偶然发现这个问题。不幸的是,Dave的代码不足以完成这项任务,即使仅在一个QScriptEngine
内进行复制也存在一些问题。所以我需要一个更复杂的版本。这些是我在解决方案中必须解决的问题:
QScriptValue
对象用于与源对象不同的QScriptEngine
,我需要一种真正复制的方法,例如功能也是如此。这可能对其他人有用,所以这里是我提出的代码:
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;
}
注意:此代码使用Qt内部方法QScriptValue::objectId()
。
答案 1 :(得分:1)
我得到了它的工作。这是解决方案,以防它对其他人有用:
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;
}
重要的部分是添加了!obj.isFunction()
检查,它只是按原样复制函数,而不是进行深层复制。这里的细微之处在于,如果项目是一个我们不想要的函数,isObject()
将返回true。这在Qt文档中有记录,我在不久前偶然发现了它。
此外,此检查消除了避免复制标记为SkipInEnumeration
的项目的需要。通过检查函数并按原样复制它们来修复无限循环。离开SkipInEnumeration
实际上打破了其他一些东西,比如eval
函数和一堆其他内置函数。