【Qt项目实战】使用脚本拓展CPP应用程序(3)——从外部控制Lua脚本中的循环中断

news/2024/12/16 2:16:20/

考古 《【Qt项目实战】使用脚本拓展CPP应用程序(2)——Lua脚本及编辑器》
考古 《【Qt项目实战 】:使用脚本拓展CPP应用程序(1)——Lua脚本及编辑器》


在本系列的前两篇我们介绍了Lua脚本编辑器的简单创建,以及Lua和C++接口的相互调用过程。本章作为前文的优化篇,介绍如何实现在C++端,优雅的中断Lua脚本中的循环过程。

一、循环中断控制

将Lua作为生产力辅助工具,我们显然不能限制只执行单一脚本,所以在多线程环境下并发运行多个Lua脚本是必备的(~这将作为我们后续章节的话题);某些场景下,我们需要在Lua脚本中使用循环方式来执行某些指令,譬如特殊状态寄存器的循环监听等
那么,自然涉及到一个问题,如何在程序结束时,优雅的中断Lua脚本中的循环控制,进而优雅的结束线程,释放资源,优雅的关闭进程呢?
答案很简单,埋点!
单就C++的程序而言,你在循环中也会这样写:

while(flag){do_something();::Sleep(100);
}void setFlag(bool flag){...}

通过控制标识符,轻松解决循环中断的问题。回到Lua脚本中,其实也是一样的策略。

可能对于新手朋友来讲,唯一麻烦的是,C++来控制Lua脚本的循环中断,听起来有点头大,其实一点也不麻烦。

二、思路及代码示例

2.1 思路1:通过标识符埋点控制循环中断

先上代码,我们有这样一段Lua脚本,其中使用了循环:

lua">local utils = QtUtils() -- 这是我们在C++注册的类,提供一些接口给Lua调用
while true doif checkStop() then print("Stopping script")breakendprint("Running...")utils:luaCallSleepMs(500)end

从上面的脚本来看,其实也是通过Flag标识符来控制循环中断。这样,思路就很清楚了,我们在C++的类型中,定义一个bool flag的成员变量,提供checkStop()方法给Lua来做检测,并提供stopScript()方法允许修改变量的值。
以下,给出简单的代码示例:

class QtBridgeLua : public QObject
{Q_OBJECT
public:void luaCallQtLoadCode(QString code);void stopScript();static int luacheckStop(lua_State *L); // 注册给Lua调用
signals:void finished(); // 可以在脚本执行完成时发送此信号
public:bool stopFlag;
};
void QtBridgeLua::luaCallQtLoadCode(QString code)
{stopFlag = false; // 开始状态if (luaL_dostring(L, code.toStdString().c_str()) != LUA_OK) {const char *error = lua_tostring(L, -1);emit lua_print_info(QString("Lua Error: ") + QString::fromUtf8(error)); // 这里我们在系列前文中已经讲过,不赘述lua_pop(L, 1); // 清除错误信息}stopFlag = false; // 结束状态emit finished();
}int QtBridgeLua::luacheckStop(lua_State *L)
{lua_getglobal(L, "qtBridgeLuaInstance");QtBridgeLua *instance = static_cast<QtBridgeLua*>(lua_touserdata(L, -1));lua_pop(L, 1);lua_pushboolean(L, instance->stopFlag);return 1;
}void QtBridgeLua::stopScript()
{stopFlag = true;
}

多线程,看情况加锁。~是不是很简单

lua_hook_88">2.2 思路2:通过 lua hook钩子触发中断

借助lua_sethook钩子我们可以实现中断。
先简单了解下lua钩子

lua_sethook 是 Lua C API 中的一个函数,用于设置钩子函数,以便在特定事件发生时调用该函数。钩子可以用于调试、性能分析、代码覆盖率等场景。以下是 lua_sethook 函数的详细使用说明。

函数原型

void lua_sethook(lua_State *L, lua_Hook f, int mask, int count);

参数说明

  1. lua_State *L: Lua 状态机的指针,表示当前的 Lua 环境。

  2. lua_Hook f: 指向钩子函数的指针。钩子函数的原型如下:

    void (*lua_Hook)(lua_State *L, lua_Debug *ar);
    
    • lua_State *L: 当前的 Lua 状态。
    • lua_Debug *ar: 指向 lua_Debug 结构的指针,包含关于当前执行状态的信息。
  3. int mask: 指定钩子触发的事件类型,可以是以下值的组合:

    • LUA_MASKLINE: 行钩子,在每执行一行代码后触发。
    • LUA_MASKCOUNT: 计数钩子,每执行指定数量的指令后触发。
    • LUA_MASKCALL: 函数调用钩子,在函数调用时触发。
    • LUA_MASKRET: 函数返回钩子,在函数返回时触发。
  4. int count: 指定计数钩子的触发频率。每执行 count 条指令后触发一次。如果 count 为 0,则不使用计数钩子。

使用示例

以下是一个使用 lua_sethook 的示例,展示了如何设置行钩子和计数钩子。

#include <lua.hpp>
#include <iostream>// 钩子函数
void myHook(lua_State *L, lua_Debug *ar) {// 获取当前执行的行号lua_getinfo(L, "l", ar);std::cout << "Line executed: " << ar->currentline << std::endl;
}int main() {lua_State *L = luaL_newstate(); // 创建 Lua 状态luaL_openlibs(L); // 打开 Lua 库// 设置行钩子lua_sethook(L, myHook, LUA_MASKLINE, 0);// 执行 Lua 代码luaL_dostring(L, "for i = 1, 5 do print(i) end");lua_close(L); // 关闭 Lua 状态return 0;
}

说明

  1. 钩子函数 myHook:

    • 这个函数在每执行一行代码时被调用,使用 lua_getinfo 获取当前行号并打印。
  2. 设置钩子:

    • lua_sethook(L, myHook, LUA_MASKLINE, 0)myHook 设置为行钩子。每当 Lua 执行一行代码时,myHook 将被调用。
  3. 执行 Lua 代码:

    • 使用 luaL_dostring 执行一段 Lua 代码。在这个示例中,Lua 将打印数字 1 到 5,每执行一行都会触发钩子。

注意事项

  • 性能影响: 使用钩子可能会影响性能,尤其是在高频率调用的情况下。应谨慎使用。
  • 调试信息: 在钩子函数中,可以使用 lua_getinfo 获取关于当前执行状态的详细信息。
  • 多次设置: 可以多次调用 lua_sethook 来更改钩子函数或事件类型。

以下,给出一些通过钩子触发中断的思路:

#include <lua.hpp>
#include <iostream>
#include <atomic>
#include <thread>// 全局变量,用于控制循环是否中断
std::atomic<bool> stopLoop(false);// 钩子函数
void myHook(lua_State *L, lua_Debug *ar) {// 检查是否需要中断循环if (stopLoop.load()) {luaL_error(L, "Loop interrupted by hook"); // 抛出错误以中断循环}
}int main() {lua_State *L = luaL_newstate(); // 创建 Lua 状态luaL_openlibs(L); // 打开 Lua 库// 设置行钩子lua_sethook(L, myHook, LUA_MASKLINE, 0);// 执行 Lua 代码luaL_dostring(L, R"(function myFunction()for i = 1, 10 doprint("Iteration: " .. i)-- 模拟一些工作os.execute("sleep 1") -- 在 Windows 上使用 os.execute("timeout 1")endendmyFunction())");// 模拟运行一段时间后中断循环std::this_thread::sleep_for(std::chrono::seconds(3));stopLoop.store(true); // 设置中断标志// 处理 Lua 错误try {luaL_dostring(L, "myFunction()");} catch (const std::exception& e) {std::cerr << "Lua Error: " << e.what() << std::endl;}lua_close(L); // 关闭 Lua 状态return 0;
}

说明

  1. 全局变量 stopLoop:

    • 使用 std::atomic<bool> 来控制循环是否中断。这个变量在 C++ 中被设置为 true 时,钩子函数会抛出错误,从而中断 Lua 的执行。
  2. 钩子函数 myHook:

    • 这个函数在每执行一行代码时被调用。它检查 stopLoop 的值,如果为 true,则调用 luaL_error 抛出错误,导致 Lua 脚本中断。
  3. 设置钩子:

    • 使用 lua_sethook(L, myHook, LUA_MASKLINE, 0)myHook 设置为行钩子。
  4. 执行 Lua 代码:

    • 在 Lua 中定义了一个简单的循环函数 myFunction,每次迭代打印当前迭代次数并模拟工作(使用 os.execute 暂停 1 秒)。
  5. 中断循环:

    • 在主线程中,等待 3 秒后设置 stopLooptrue,这将导致钩子函数抛出错误,从而中断 Lua 的执行。

注意事项

  • 错误处理: 在 Lua 中抛出错误后,确保在 C++ 中捕获并处理这些错误,以避免程序崩溃。
  • 性能影响: 使用钩子可能会影响性能,尤其是在高频率调用的情况下。应谨慎使用。
  • 多线程: 如果在多线程环境中使用,确保对共享变量的访问是线程安全的。

通过这种方式,你可以在 Lua 脚本中使用 lua_sethook 设置钩子,以便在特定条件下中断循环。


业精于勤荒于嬉。否,勤于嬉戏,将此当做喜爱的游戏,会有不一样的感受~诸君共勉。


http://www.ppmy.cn/news/1555467.html

相关文章

计算机组成原理与系统结构——微程序控制

笔记内容及图片整理自XJTUSE “计算机组成原理与系统结构” 课程ppt&#xff0c;仅供学习交流使用&#xff0c;谢谢。 基本概念 微指令 将控制单元实现为基本逻辑单元之间的互连并非易事&#xff0c;且设计相对呆板&#xff0c;难以灵活地改变&#xff0c;因此实现微程序控制…

计算机网络技术基础:2.计算机网络的组成

计算机网络从逻辑上可以分为两个子网&#xff1a;资源子网和通信子网。 一、资源子网 资源子网主要负责全网的数据处理业务&#xff0c;为全网用户提供各种网络资源与网络服务。资源子网由主机、终端、各种软件资源与信息资源等组成。 1&#xff09;主机 主机是资源子网的主要…

爬虫基础之Web网页基础

网页的组成 网页可以分为三大部分–HTML、CSS 和 JavaScript。如果把网页比作一个人&#xff0c;那么 HTML 相当于骨架、JavaScript 相当于肌肉、CSS 相当于皮肤&#xff0c;这三者结合起来才能形成一个完善的网页。下面我们分别介绍一下这三部分的功能。 HTML HTML(Hypertext…

一些常见网络安全术语

1、黑帽 为非法目的进行黑客攻击的人&#xff0c;通常是为了经济利益。他们进入安全网络以销毁&#xff0c;赎回&#xff0c;修改或窃取数据&#xff0c;或使网络无法用于授权用户。这个名字来源于这样一个事实&#xff1a;老式的黑白西部电影中的恶棍很容易被电影观众识别&…

自适应卡尔曼滤波(包括EKF、UKF、CKF等)的创新思路——该调什么、不该调什么

在调节自适应卡尔曼滤波时&#xff0c;需要注意的参数和矩阵都对滤波器的性能有直接影响。本文给出详细的说明&#xff0c;包括相关公式和 MATLAB 代码示例 文章目录 需要调节的参数1. **过程噪声协方差矩阵 Q Q Q**&#xff1a;2. **测量噪声协方差矩阵 R R R**&#xff1a;…

安装一个tensorflow 1X 版本

1. 查询版本对应 2.创建虚拟环境&#xff0c;激活虚拟环境 conda create -n tensor110 python3.63. 安装tensorflow1.10 pip install tensorflow1.10

调试 Simple RNN 环境及解决依赖冲突问题的总结

调试 Simple RNN 环境及解决依赖冲突问题的总结 摘要 在深度学习领域&#xff0c;Simple RNN&#xff08;简单循环神经网络&#xff09;是一种常用于序列建模的基础网络结构。然而&#xff0c;由于其对环境配置的依赖较强&#xff0c;实际运行中可能因依赖冲突导致代码无法正…

发现一个对话框中的按钮,全部失效,点击都没有任何反应,已经解决

前端问题&#xff0c;技术vue2&#xff0c;ts。 发现一个对话框中的按钮&#xff0c;全部失效&#xff0c;点击都没有任何反应。 因为我只在template标签中加入下面这个代码&#xff0c;并没有注册。 只要有一个子组件没有注册&#xff0c;就会影响所有的按钮&#xff0c;使当前…