当前位置 : 主页 > 网络编程 > lua >

使用Lua和C管理堆栈

来源:互联网 收集:自由互联 发布时间:2021-06-23
我需要传递lua脚本一个字符串(文件路径),并返回0到许多字符串. int error = 0;lua_State *L = lua_open();luaL_openlibs(L);std::vectorstring list_strings; 在加载和调用源文件之前,用于将字符串压入堆栈 if
我需要传递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,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函数从堆栈中取出该值,并将解析与该路径关联的文件.

在解析之后,它应该返回一个包含其内部字符串的表(您可以将其视为搜索特定字符串的函数,我想)

编辑:做得更清楚.

有什么建议/资源吗?
这里有类似的问题吗?或任何有用的资源?

我想确保在看到你出错的地方之前我明白你在做什么.你有一个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的默认分配方案可能会使大数据集的分配数量超过您.所以在这种情况下,我不会打扰保留.

然而,从一个健康规模的储备开始并不是不明智的,作为一种默认情况.选择一个你认为“足够大”的数字,并保留那么多空间.

网友评论