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,1,1,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函数从堆栈中取出该值,并将解析与该路径关联的文件.
在解析之后,它应该返回一个包含其内部字符串的表(您可以将其视为搜索特定字符串的函数,我想)
编辑:做得更清楚.
有什么建议/资源吗?
这里有类似的问题吗?或任何有用的资源?
好的,让我们从顶部开始:
if ((error = lua_pcall(L, 1, LUA_MULTRET, 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, 1, 1, 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);,依赖于emplace_back
函数来就地构造std :: string.这样可以避免制作更多的字符串副本.
我们需要做最后一点清理工作.我们的堆栈上还有两个值:字符串和表.我们完成了字符串,所以我们需要摆脱它.这是通过从Lua堆栈弹出一个条目来完成的:
lua_pop(L, 1);
这里,“1”是要弹出的条目数,而不是堆栈位置.
您是否了解堆栈管理现在如何在Lua中运行?
1) Looking at the state of the stack before the call… luaL_loadfile pushes a function to the stack? Or does lua_pcall?
假设除了创建它之外你还没有对Lua状态做过任何事情,那么在luaL_loadfile之前堆栈是空的.是的,luaL_loadfile将一个函数推送到堆栈.此函数表示已加载的文件.
3) What would the result of the stack be, if after making the function call it returned an error value?
究竟是什么the documentation says.现在您已经了解了堆栈的工作原理,您应该阅读文档.还建议使用Lua中的编程.版本5.0是available online for free,但5.1书需要花钱. 5.0本书仍然是一个有用的起点.
4) list_strings.reserve(len); As for this… This lua script is actually embedded in a small C program that recurses through a code base and will collect ALL of the strings that the lua script returns from ALL of the files… I don’t know exactly how reserve works, but What I’m saying is that I will be using many tables to add strings to this list… Should reserve just be not used in that case? or still used…
std :: vector :: reserve确保std :: vector至少包含X元素的空间,其中X是传递它的值.我这样做是因为Lua告诉你表中有多少元素,所以没有必要让std :: vector自行扩展.你可以让它为一切做一个内存分配,而不是让std :: vector :: push_back函数根据需要分配更多的内存.
只要您调用一次Lua脚本,这就很有用.也就是说,它从Lua获得单个返回值.无论返回的表有多大,这都行.如果你多次调用你的Lua脚本(来自C),那么就没有办法提前知道要保留多少内存.您可以为每个返回的表保留空间,但是std :: vector的默认分配方案可能会使大数据集的分配数量超过您.所以在这种情况下,我不会打扰保留.
然而,从一个健康规模的储备开始并不是不明智的,作为一种默认情况.选择一个你认为“足够大”的数字,并保留那么多空间.