gpt4 book ai didi

c++ - 使用Lua和C++管理堆栈

转载 作者:行者123 更新时间:2023-12-04 08:59:05 30 4
gpt4 key购买 nike

我需要将单个字符串(文件路径)传递给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将弹出两件事。然后它将正好将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接口(interface)的代码中也是如此。所以我尽可能晚地翻译。但是您不必。

现在,了解循环的内容。第一步是从表中获取表条目。为此,我们需要给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::string可以。这就是为什么我不像您那样使用 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获得单个返回值。无论返回的表有多大,这都将起作用。如果您多次(从C++)调用Lua脚本,则无法提前知道要保留多少内存。您可以为返回的每个表保留空间,但是 std::vector的默认分配方案有可能在大型数据集的分配数量上胜过您。因此,在那种情况下,我不会理会 reserve

但是,从默认情况下开始,使用大小合适的 reserve开始并不是不明智的。选择一个您认为足够“足够大”的数字,并保留足够的空间。

关于c++ - 使用Lua和C++管理堆栈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6511432/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com