5

自動化のためにQtScriptを使用するプログラムがあります。スクリプトが次のようにアクセスできるように、スクリプトエンジンのグローバルスコープに多数のC++関数とクラスを追加しました。

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

複数のスクリプトを連続して実行できるようにしたいのですが、それぞれが新しいグローバル状態になっています。したがって、1つのスクリプトがグローバル変数を設定する場合

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は実際の関数ポインターを取ります)。

4

2 に答える 2

2

QtScript内でマルチスレッドを利用できるようにするために、QScriptValueオブジェクトを別のオブジェクトにディープコピーする方法が必要でありQScriptEngine、この質問に遭遇しました。残念ながら、Daveのコードはこのタスクには不十分であり、1つだけでコピーする場合でもいくつかの問題がありますQScriptEngine。そのため、より洗練されたバージョンが必要でした。これらは、ソリューションで対処しなければならなかった問題です。

  1. オブジェクトにそれ自体への参照が含まれている場合、Daveのコードはスタックオーバーフローを引き起こします。
  2. 1つのオブジェクトへの複数の参照によって、参照されるオブジェクトが複数回コピーされないように、ソリューションがオブジェクトへの参照を尊重するようにしたかったのです。
  3. ディープコピーされたオブジェクトはソースオブジェクトと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-internalメソッドを使用しQScriptValue::objectId()ます。

于 2016-02-27T04:22:31.430 に答える
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

于 2011-02-16T21:34:55.333 に答える