使用Lua和C管理堆栈
我需要传递lua脚本一个字符串(文件路径),并返回0到许多字符串.
int error = 0; lua_State *L = lua_open(); luaL_openlibs(L); std::vector<string> list_strings; 在加载和调用源文件之前,用于将字符串压入堆栈 if ((error = luaL_loadfile(L,"src/test.lua")) == 0) { lua_pushstring(L,path.c_str()); if ((error = lua_pcall(L,1,LUA_MULTRET,0)) == 0) { lua_gettable(L,LUA_GLOBALSINDEX); lua_pcall(L,0); if (lua_gettop(L) == 1 && lua_istable(L,1)) { int len = lua_objlen(L,1); for (int i=1;i =< len; i++) { lua_pushinteger(L,i); lua_gettable(L,1); const char *s = lua_tostring(L,-1); if (s) { list_strings.push_back(s); } lua_pop(L,1); } } } } 就目前而言,我刚刚从示例中复制代码,所以我不确定我正在做的是我想做什么…我想把路径推到堆栈上,并调用一个lua函数从堆栈中取出该值,并将解析与该路径关联的文件. 在解析之后,它应该返回一个包含其内部字符串的表(您可以将其视为搜索特定字符串的函数,我想) 编辑:做得更清楚. 有什么建议/资源吗? 解决方法
我想确保在看到你出错的地方之前我明白你在做什么.你有一个Lua脚本文件.您想要执行此脚本,并传递一个字符串参数.它将执行一些操作,然后返回零个或多个字符串作为返回值.并且您希望在代码中获取这些值.
好的,让我们从顶部开始: if ((error = lua_pcall(L,0)) == 0) 通常,当您执行lua_pcall时,第三个参数会告诉Lua确切的预期返回值.如果被调用的函数返回的值超过此数字,则丢弃这些返回值.如果它返回的数量少于此数字,则使用其他NIL值来填充计数. LUA_MULTRET告诉Lua不要这样做.使用它时,所有结果都会被压入堆栈. 现在,既然你没有发布你的脚本,我必须猜测你的脚本是什么样的.你正在返回多个字符串,但你永远不会说这是怎么回事. Lua作为一种语言,允许多个返回值: return "string1","string2"; 这导致2个字符串被压入堆栈.这不同于: return {"string1","string2"}; 这将一个对象放入堆栈:一个表.该表包含2个字符串.看到不同? 看看你的代码,看起来你希望Lua脚本返回一个字符串表,而不是多个返回值. 在这种情况下,您应该像这样调用您的Lua脚本: if ((error = lua_pcall(L,0)) == 0) 这告诉Lua你期望一个返回值,如果用户没有提供一个,Lua会将NIL推送到堆栈. 现在让我们谈谈堆栈.在发出函数调用之前,堆栈的状态是这样的: 2- {string: path.c_str()} 1- {function: loaded from file "src/test.lua"} 这是从堆栈的顶部到“底部”.如果你使用我给你的lua_pcall,你将在你的堆栈上获得以下内容: 1- {return value} lua_pcall将从堆栈中删除参数和函数.因此它将从堆栈中弹出N 1个项目,其中N是由lua_pcall(第二个参数)指定的Lua函数的参数个数.因此,Lua将从堆栈中弹出2件事.然后它会将1个值精确地推送到堆栈:返回值(如果没有返回值则为NIL). 这样我们就可以通过函数调用了.如果一切顺利,我们现在希望堆栈包含: 1- {table: returned from function} 但是,一切都可能不顺利.该脚本可能已返回NIL.或者是其他东西;不能保证它是一张桌子.因此,下一步是验证返回值(注意:这是您的代码停止有意义的地方,所以这是全新的). if(lua_istable(L,-1)) lua_istable正如名称所暗示的那样:确定给定项是否为表.但是“-1”是什么意思,为什么它不是你代码中的“1”? 此参数是对堆栈上的位置的引用. Lua的堆栈也是Lua的寄存器文件.这意味着,与实际堆栈不同,您可以在堆栈上的任何元素处达到峰值.堆栈上的元素在堆栈上具有绝对位置.现在,这是我们的堆栈再次出现的情况: 1- {return value} 我写的“1”是该值堆栈的绝对位置.我可以推送值和弹出值,但除非我弹出这个值,否则它的位置将始终为“1”. 但是,它只是“1”,因为我们的堆栈开始是空的.假设这一点有点粗鲁(因为如果堆栈不是空的话,它可以真的咬你.当你可以假设堆栈真的是空的时候,或者如果它不是,堆栈中已有的东西,Lua文档会有用地说明.)因此,您可以使用相对位置. 这就是“-1”:它是堆栈顶部的第一个堆栈索引.我们上面定义的lua_pcall函数将从堆栈(参数和函数)中弹出2个项目,并按下1项(返回值或NIL).因此,“ – 1”将始终引用我们的返回值. 因此,我们检查堆栈索引“-1”(堆栈顶部)是否为表.如果不是,则失败.如果是,那么我们可以解析我们的列表. 这就是我们列出解析的地方.第一步是获取列表中的项目数: int len = lua_objlen(L,-1); list_strings.reserve(len); 第二个只是一个非常好的,所以你不会分配很多次.您确切知道该列表中将包含多少字符串,因此您也可以提前知道该列表,对吧? lua_objlen获取表中数组元素的数量.请注意,这可以返回零,但我们的循环将处理该情况. 接下来,我们走出桌子,拔出琴弦. for (int i=0; i < len; i++) { //Stuff from below. } 请记住,Lua使用1个基数的索引.我个人更喜欢在C/C++代码中使用0基索引,甚至是与Lua接口的代码.所以我尽可能晚地进行翻译.但你没必要. 现在,为循环的内容.第一步是从表中获取表条目.要做到这一点,我们需要给Lua一个索引并告诉Lua从表中获取该索引: lua_pushinteger(L,i + 1); lua_gettable(L,-2); 现在,第一个函数将索引压入堆栈.之后,我们的堆栈看起来像这样: 2- {integer: i + 1} 1- {table: returned from function} lua_gettable函数值得更多解释.它需要一个键(记住:Lua中的表键不必是整数)和一个表,并返回与该表中该键相关的值.或NIL,如果没有关联的值.但它的工作方式有点奇怪. 它假设堆栈的顶部是关键.因此,它所采用的参数是密钥将索引到的表的堆栈位置.我们使用“-2”因为,看看堆栈.因为我们推了一个整数,所以表从顶部开始是2;因此我们使用“-2”. 在此之后,我们的堆栈看起来像这样: 2- {value: from table[i + 1]} 1- {table: returned from function} 现在我们已经获得了一个值,我们必须验证它是一个字符串,然后获取它的值. size_t strLen = 0; const char *theString = lua_tolstring(L,-1,&strLen); 该功能可以同时完成所有这些操作.如果我们从表中获得的值不是字符串(或数字,因为Lua会自动将数字转换为字符串),那么theString将为NULL.否则,theString将拥有一个Lua拥有的指针(不要删除)到字符串. strLen也将具有字符串的长度. 快速放弃:Lua字符串以NULL结尾,但它们也可以在内部包含NULL字符.不允许使用C字符串,但C std :: strings是.这就是为什么我不像你那样使用lua_tostring; C字符串可以完全按原样存储Lua字符串. 现在我们有了Lua的字符串数据,我们需要把它放到我们的列表中.为避免不必要的副本,我更喜欢这种语法: list_strings.push_back(); list_strings.back().assign(theString,strLen); 如果我使用的是支持C 11的标准库和编译器,我会使用list_strings.emplace_back(theString,strLen);,依赖于 我们需要做最后一点清理工作.我们的堆栈上还有两个值:字符串和表.我们完成了字符串,所以我们需要摆脱它.这是通过从Lua堆栈弹出一个条目来完成的: lua_pop(L,1); 这里,“1”是要弹出的条目数,而不是堆栈位置. 您是否了解堆栈管理现在如何在Lua中运行?
假设除了创建它之外你还没有对Lua状态做过任何事情,那么在luaL_loadfile之前堆栈是空的.是的,luaL_loadfile将一个函数推送到堆栈.此函数表示已加载的文件.
究竟是什么the documentation says.现在您已经了解了堆栈的工作原理,您应该阅读文档.还建议使用Lua中的编程.版本5.0是available online for free,但5.1书需要花钱. 5.0本书仍然是一个有用的起点.
std :: vector :: reserve确保std :: vector至少包含X元素的空间,其中X是传递它的值.我这样做是因为Lua告诉你表中有多少元素,所以没有必要让std :: vector自行扩展.你可以让它为一切做一个内存分配,而不是让std :: vector :: push_back函数根据需要分配更多的内存. 只要您调用一次Lua脚本,这就很有用.也就是说,它从Lua获得单个返回值.无论返回的表有多大,这都行.如果你多次调用你的Lua脚本(来自C),那么就没有办法提前知道要保留多少内存.您可以为每个返回的表保留空间,但是std :: vector的默认分配方案可能会使大数据集的分配数量超过您.所以在这种情况下,我不会打扰保留. 然而,从一个健康规模的储备开始并不是不明智的,作为一种默认情况.选择一个你认为“足够大”的数字,并保留那么多空间. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |