在为 QScriptEngine 重新定义 “print()” 函数时返回“未定义的值”有什么意义?

What's the point of returning an "Undefined Value" when re-defining "print()" function for QScriptEngine?

本文关键字:未定义的值 什么 未定义 函数 新定义 QScriptEngine 定义 在为 print 返回      更新时间:2023-09-26

[背景]

默认print()函数QScriptEngine将结果打印到Qt Creator IDE的终端进行调试。因此,如果我们要自己制作 ECMA 脚本解释器,则必须将输出重定向到我们的文本编辑器。

文档">使应用程序可编写脚本"的这一部分自Qt 4.3以来保持不变。

">重新定义打印(("部分:

Qt Script提供了一个内置的print((函数,可用于 简单的调试目的。内置的 print(( 函数写入 标准输出。您可以重新定义 print(( 函数(或添加您的 自己的函数,例如 debug(( 或 log((( 将文本重定向到 别处。以下代码显示了一个自定义 print((,它添加了 文本到 QPlainTextEdit。

因此,这是建议重新定义print()

QScriptValue QtPrintFunction(QScriptContext *context, QScriptEngine *engine)
 {
     QString result;
     for (int i = 0; i < context->argumentCount(); ++i) {
         if (i > 0)
             result.append(" ");
         result.append(context->argument(i).toString());
     }
     QScriptValue calleeData = context->callee().data();
     QPlainTextEdit *edit = qobject_cast<QPlainTextEdit*>(calleeData.toQObject());
     edit->appendPlainText(result);
     return engine->undefinedValue();
 }

起初,我怀疑是否需要通过return engine->undefinedValue();返回"未定义的值",看起来参数*engine的作用只是返回这个空值。

因此,这是我为更改功能所做的工作:

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    /*
    QScriptValue calleeData = context->callee().data();
    QPlainTextEdit *edit = qobject_cast<QPlainTextEdit*>(calleeData.toQObject());
    edit->appendPlainText(result);
    return engine->undefinedValue();
    */
    return engine->toScriptValue(result); // ---> return the result directly
}

我认为这对我来说更合理:从脚本引擎返回一个评估QScriptValue,稍后可以将该值转换为 QString 以进行输出。这绕过了动态类型转换的需要,动态类型转换可能会变得混乱,特别是对于自定义 QObject。

对于这两种打印函数,以下是对脚本引擎的阐述:

 QScriptEngine *engine = new QScriptEngine(this); 
 QTextEdit *input = new QTextEdit(this);
 QTextEdit *output = new QTextEdit(this);
 // Use documented print function : 
 QScriptValue fun = engine->newFunction(QtPrintFunction);
 // Use my revised print function : 
 // QScriptValue fun = engine->newFunction(myPrintFunction);
 fun.setData(engine->newQObject(output));
 engine->globalObject().setProperty("print", fun);

评估和输出:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
output->append(result.toString());

[可编译代码]

(需要Qt版本>4(

test.pro

QT += core gui widgets script
TARGET = Test
TEMPLATE = app
SOURCES += main.cpp'
        console.cpp
HEADERS  += console.h

主.cpp

#include <QApplication>
#include "console.h"
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Console w;
    w.show();
    return app.exec();
}

控制台.h

#ifndef CONSOLE_H
#define CONSOLE_H
#include <QWidget>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QPushButton>
#include <QScriptEngine>
class Console : public QWidget
{
    Q_OBJECT
public:
    Console();
    ~Console();
public slots:
    void runScript();
private:
    QScriptEngine *engine;
    QVBoxLayout *layout;
    QPushButton *run;
    QTextEdit *input, *output;
};
QScriptValue QtPrintFunction(QScriptContext *context, QScriptEngine *engine);
QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine);
#endif // CONSOLE_H

控制台.cpp

#include "console.h"
Console::Console()
{
    engine = new QScriptEngine(this);
    layout = new QVBoxLayout(this);
    run = new QPushButton("Run",this);
    input = new QTextEdit(this);
    output = new QTextEdit(this);
    layout->addWidget(input);
    layout->addWidget(run);
    layout->addWidget(output);
    //QScriptValue fun = engine->newFunction(QtPrintFunction);
    QScriptValue fun = engine->newFunction(myPrintFunction);
    fun.setData(engine->newQObject(output));
    engine->globalObject().setProperty("print", fun);
    connect(run, SIGNAL(clicked()), this, SLOT(runScript()));
}
void Console::runScript()
{
    QString command = input->toPlainText();
    QScriptValue result = engine->evaluate(command);
    output->append(result.toString());
}
QScriptValue QtPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    QScriptValue calleeData = context->callee().data();
    QTextEdit *edit = qobject_cast<QTextEdit*>(calleeData.toQObject());
    edit->append(result);
    return engine->undefinedValue();
}
QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    return engine->toScriptValue(result);
}
Console::~Console()
{
}


[示例]

输入 1:

print(123);

输出(Qt文档QtPrintFunction()(:

123
undefined

输出(我的版本 myPrintFunction() (:

123

输入 2:

for (i = 0; i < 3; i++)
    print(i);

输出(Qt文档QtPrintFunction()(:

0

1

阿拉伯数字

定义

输出 ( myPrintFunction() (:

阿拉伯数字


输入 3:

print("Stack");
print("Overflow");

输出(Qt文档QtPrintFunction()(:

溢出

定义

输出(我的版本 myPrintFunction()(:

溢出


[问题]

尽管myPrintFunction起初似乎工作正常,但当脚本中调用了两个以上的print时,它不起作用,其中只会执行最后print

似乎返回"未定义的值"对于打印功能是必要的。但是为什么???

并不是说有必要返回undefinedValue(),但是当你这样做时,这与不返回任何东西是一样的。或者本质上,好像你声明了函数 void print(...) ,可以这么说。

这就是QtPrintFunction所做的 - 它返回"无"。但它确实有一个副作用,即在调用它时将其参数附加到内部数据对象。这就是在 output 对象中获取传递给print的所有值的原因。

现在,当您调用engine->evaluate()时,它将返回最后一个计算表达式的值。因此,使用myPrintFunction,您只能获得最后一个值。

因此,如果您要输入以下内容:

print("Stack");
print("Overflow");
"garbage";

你只会得到garbage(双关语(,因为这是最后一个计算的表达式。

但是,如果您要输入以下内容:

print("Stack") + ''n' +
print("Overflow");

正如您所期望的那样,您将获得这两个值。

此外,如果您输入:

result = "";
for (i = 0; i < 3; i++)
    result += print(i) + ''n';

你也会得到你所期望的。

希望这能解释为什么你的函数会像现在这样运行。

但是,我不认为这是您要实现的目标。 所以... 继续前进。

您可以做的一件事是按如下方式定义myPrintFunction

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    static QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    result.append(''n');
    return engine->toScriptValue(result);
}

这将按照您期望的方式"工作"。唯一的问题是您无法清除result的值。如果这对你有用,那么就是这样。

还有一种更好的方法可以做到这一点,这可能是定义一个类,例如:

class QTrace: public QObject
{
    ...
    void clear();
    void append(const QString& value);
    const QString& get();
}

并将该类的对象传递给fun.setData(engine->newQObject(trace))并将函数定义为:

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    result.append(''n');
    QScriptValue calleeData = context->callee().data();
    QTrace *trace = qobject_cast<QTrace*>(calleeData.toQObject());
    trace->append(result);
    return engine->undefinedValue();
}

最后,您可以将runScript函数更改为以下内容:

trace->clear();
QScriptValue result = engine->evaluate(command);
if(result.isError())
    output->append(result.toString());
else
    output->append(trace->get());

或者可能还有其他方法,但希望能帮助你让球朝着正确的方向滚动。

快速回答:你不需要返回undefinedValue。您可以退回任何您想要的东西。但是,engine->evaluate()只能返回一个值,我认为这是混淆的根源。

看看评估代码:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
output->append(result.toString());

这将获取一个脚本字符串并对其进行计算,将结果值分配给 result ,然后将其追加到QTextEdit控件。evaluate() 的返回值将是脚本的最后一个值。例如:

QString command = "var a=1, b=2; a; b;";
QScriptValue result = engine->evaluate(command);
output->append(result.toString());

result将包含 2,然后记录到 QTextEdit 控件。

那么,这是怎么回事呢?以这个输入为例:

print("Stack");
print("Overflow");

使用 QtPrintFunction 时,"堆栈"和"溢出"作为QtPrintFunction实现的一部分添加到output控件中。当evaluate()结束时,print("Overflow")最后一个语句返回 undefined。然后,计算代码获取该返回值并将其添加到output,结果为:

Stack
Overflow
undefined

当使用myPrintFunction时,该实现不会向output记录任何内容,而是返回值。结果是 evaluate() 函数仅返回最后一个值("溢出"(,导致以下输出:

Overflow

这就是你所看到的。

由于您要将输出重定向到您自己的自定义文本编辑器,因此您需要更改以下代码块:

QScriptEngine *engine = new QScriptEngine(this); 
 QTextEdit *input = new QTextEdit(this);
 //QTextEdit *output = new QTextEdit(this);
 YourCustomEditor *output = getYourCustomEditor();
 // Use my revised print function : 
 QScriptValue fun = engine->newFunction(myPrintFunction);
 fun.setData(engine->newQObject(output)); // pass your editor in
 engine->globalObject().setProperty("print", fun);

然后,在myPrintFunction中,您需要以类似于QtPrintFunction的方式将输出发送到YourCustomEditor。然后你不再需要从evaluate()输出结果:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
// output->append(result.toString()); <- not needed anymore

我以前曾多次使用嵌入式口译员,事情很快就会变得混乱。希望这足够清楚,可以提供帮助。