我在应用程序中使用 Qt 脚本引擎作为用户访问其功能的替代方式。因此,我将一些 C++ 类导出到 Qt ScriptEngine,它将用作应用程序的接口。问题是,这些 C++ 类可能会引发异常。
我有一个在自己的线程上运行的“ScriptInterface”类,监听处理脚本的请求。因此,当我评估用户的脚本时,我在它周围有一个 try/catch 块来处理异常,并将错误打印到应用程序中的控制台。
...
try {
m_engine->evaluate(script, name);
}
catch (Exception const& e) {
// deal with it
}
catch (...) {
// scary message
}
这在 Windows 中完美运行...但在 Linux 中不起作用 - 程序终止并显示以下消息:
terminate called after throwing an instance of 'Basilisk::InvalidArgumentException'
what(): N8Basilisk24InvalidArgumentExceptionE
Aborted
我有预感,这是因为异常冒泡到事件处理程序(因为脚本引擎使用信号来调用函数)在我导出的类中),所以我重新实现了 QApplication::notify 来处理那里的异常,但它们没有被捕获。
我的问题是,我是否做了一些根本性错误的事情?另外,作为替代方案,是否可以从我的 C++ 类中显式抛出脚本异常?
提前致谢
编辑:修复了描述以包含 catch(...) 语句。
更新(解决方案):我通过遵循与接受的答案中概述的策略类似的策略“修复”了这个问题。虽然我还没有探究为什么在 linux 上没有捕获异常的根源(我现在怀疑 m_engine->evaluate 在 linux 上产生了一个单独的线程),但我已经开始使用预期的 Qt 脚本中抛出异常的方式,即 QScriptContext::throwError()
。
如果我的函数看起来像这样:(随机示例)
void SomeClass::doStuff(unsigned int argument) {
if (argument != 42) {
throw InvalidArgumentException(
"Not the answer to Life, the Universe and Everything.");
}
// function that is not part of the scripting environment,
// and can throw a C++ exception
dangerousFunction(argument);
}
现在是这样:(特别注意返回类型)
QScriptValue SomeClass::doStuff(unsigned int argument) {
if (argument != 42) {
// assuming m_engine points to an instance of
// QScriptEngine that will be calling this function
return m_engine->currentContext()->throwError(QScriptContext::SyntaxError,
"Not the answer to Life, the Universe and Everything.");
}
try {
// function that is not part of the scripting environment,
// and can throw a C++ exception
dangerousFunction(argument);
} catch (ExpectedException const& e) {
return m_engine->currentContext()->throwError(QScriptContext::UnknownError,
e.message());
}
// if no errors returned, return an invalid QScriptValue,
// equivalent to void
return QScriptValue();
}
那么在哪里处理这些脚本错误呢?调用QScriptEngine::evaluate()
后,您可以检查是否有任何未捕获的异常,使用QScriptEngine::hasUncaughtException()
,通过uncaughtException(),现在您已获得脚本中发生错误的消息、跟踪和行号!
希望这可以帮助别人!
I am using the Qt script engine in my application as an alternative way for the user to access its functionality. As such, I export some C++ classes to the Qt ScriptEngine, that will serve as the interface to the application. The problem is, these C++ classes can throw exceptions.
I have a "ScriptInterface" class running on its own thread, listening for requests to process scripts. So when I evaluate a user's script, I have a try/catch block around it to handle exceptions, and print the error to the console in the application.
...
try {
m_engine->evaluate(script, name);
}
catch (Exception const& e) {
// deal with it
}
catch (...) {
// scary message
}
This works perfectly in windows... but doesn't work in linux- the program terminates with this message:
terminate called after throwing an instance of 'Basilisk::InvalidArgumentException'
what(): N8Basilisk24InvalidArgumentExceptionE
Aborted
I had a hunch that it was because the exceptions bubbled up to the event handler (since the script engine uses signals to call the functions in my exported classes), so I reimplemented QApplication::notify, to handle exceptions there, but they weren't caught.
My question is, am I doing something fundamentally wrong? Also, as an alternative, is it possible to explicitly throw script exceptions from within my C++ classes?
Thanks in advance
EDIT: fixed the description to include the catch(...) statement.
UPDATE (SOLUTION): I "fixed" this problem by following a strategy similar to the one outlined in the accepted answer. Although I haven't gone to the source of why the exceptions don't get caught on linux (my suspicion now, is that m_engine->evaluate spawn a seperate thread on linux), but I have started using the intended way of exception throwing in Qt Scripts, and that is QScriptContext::throwError()
.
In cases where my function would look like this: (random example)
void SomeClass::doStuff(unsigned int argument) {
if (argument != 42) {
throw InvalidArgumentException(
"Not the answer to Life, the Universe and Everything.");
}
// function that is not part of the scripting environment,
// and can throw a C++ exception
dangerousFunction(argument);
}
It is now this: (pay particular attention to the return type)
QScriptValue SomeClass::doStuff(unsigned int argument) {
if (argument != 42) {
// assuming m_engine points to an instance of
// QScriptEngine that will be calling this function
return m_engine->currentContext()->throwError(QScriptContext::SyntaxError,
"Not the answer to Life, the Universe and Everything.");
}
try {
// function that is not part of the scripting environment,
// and can throw a C++ exception
dangerousFunction(argument);
} catch (ExpectedException const& e) {
return m_engine->currentContext()->throwError(QScriptContext::UnknownError,
e.message());
}
// if no errors returned, return an invalid QScriptValue,
// equivalent to void
return QScriptValue();
}
So where does one deal with these script errors? After the call to QScriptEngine::evaluate()
you can check whether there are any uncaught exceptions, with QScriptEngine::hasUncaughtException()
, obtain the error object with uncaughtException()
, and now you have the message, the trace, and line number in the script where the error occured!
Hope this helps someone out!
发布评论
评论(2)
当我尝试使用 SWIG 和 Python 来包装 C++ 库时,我遇到了类似的问题。最终发生的事情是,我为所有包装的类创建了一个存根,这些类捕获了异常并静静地失败了。幸运的是,我拥有只传递容器类和状态模式对象的包装功能,因此我可以轻松检查是否有问题。我可以给你同样的建议吗?
是的,如果您授予脚本引擎访问异常工厂(一个唯一目的是抛出 C++ 异常的类)的权限,脚本引擎很可能会抛出 C++ 异常。
I ran into a similar type of problem when trying to use SWIG with Python to wrap C++ libraries. Eventually what happened was that I made a stub for all the wrapped classes which caught the exception and failed quietly. Luckily I had the luxury of wrapping functionality which only passed container classes and state pattern objects, so I could easily check if something was amiss. May I suggest the same for you?
And yes, it's very possible for a script engine to throw C++ exceptions if you've given it access to an exception factory (a class whose sole purpose is to throw C++ exceptions.)
在调试器下运行程序,并在运行时库的 Terminate() 函数中放置一个断点。这样,您将在调试器中的终止()处停止,并通过检查调用堆栈,您将看到终止()被调用的位置。
Run your program under a debugger and place a breakpoint inside your runtime library's terminate() function. That way you'll stop on terminate() in the debugger and by inspecting the call stack you will then see from where terminate() was called.