如何使用C-API在Lua 5.1中创建嵌套表?

4

我需要在Lua5.1 C-API中创建这样的构造,而不是在Lua 5.2及以上版本中。

a = {["b"] = {["c"] = {["d"] = {["e"] = "GOOD"}}}}

print(a.b.c.d.e);

期望结果:好

感谢您的回答!


可能是如何使用C API创建嵌套的Lua表的重复问题。 - siffiejoe
1个回答

11
Lua C API基于栈。这意味着大多数C API函数的行为不仅取决于给定的参数,还取决于Lua堆栈的内容(它是常被称为“L”的lua_State*变量的一部分)。特定API函数对于堆栈内容的期望以及它如何影响堆栈元素,您需要在Lua手册中查找。
让我们开始创建一个表并将其分配给全局变量a:
lua_newtable( L );
lua_setglobal( L, "a" );

这等同于Lua代码片段:a = {}

lua_newtable()的文档告诉我们,该函数将新表推到Lua堆栈的顶部。这与lua_setglobal()非常匹配,它从堆栈顶部弹出一个值并将其赋给指定名称的全局变量。因此,两个函数结合起来(如上面的代码片段)在堆栈效果方面是平衡的。 堆栈平衡代码的好处是可以将它们插入任何位置,组合后的代码仍然有效。 (一般规则是:只要组合后的堆栈效果相同,就可以用一系列语句替换单个语句,反之亦然。)例如:

lua_newtable( L );  /* ==> stack: ..., {} */
lua_pushnil( L ); /* ==> stack: ..., {}, nil */
lua_pop( L, 1 ); /* ==> stack: ..., {} */
lua_setglobal( L, "a" ); /* ==> stack: ... */

由于lua_pushnil(L);lua_pop(L,1);组合在一起不改变Lua栈的内容,因此仍会将表分配给全局变量a。我们将添加代码,在将其推送到栈上之后,在将其从栈中移除并分配给全局变量之前,修改表。如我所说,您可以在两个Lua C API函数之间的任何地方插入平衡栈的代码段,因此您只需要确定包含所有所需元素的堆栈的正确位置。

我们想要向键为"b"的表中添加一个字段和另一个表作为值。用于执行此操作的Lua C API函数是lua_settable()(还有其他适用于特定密钥类型的便利函数,例如lua_setfield(),但我们将在此处使用lua_settable())。lua_settable()需要将存储键/值对的表放在Lua堆栈的某个位置(这通常意味着您必须将堆栈索引作为参数传递),并将键和值作为堆栈上面的两个元素。键和值(但不是表)都将被lua_settable()弹出:

lua_newtable( L );  /* ==> stack: ..., {} */
lua_pushliteral( L, "b" ); /* ==> stack: ..., {}, "b" */
lua_newtable( L ); /* ==> stack: ..., {}, "b", {} */
lua_settable( L, -3 ); /* ==> stack: ..., {} */
lua_setglobal( L, "a" ); /* ==> stack: ... */

等价的 Lua 代码是 a = { b = {} }

通常情况下,您不需要真正关心 Lua 栈中某个位置以下的内容,这就是相对于栈顶的索引(在上面的代码片段中为 -3)发挥作用的地方。 -3 是指刚好在键 "b" 下面的表(位于 -2),并且在另一个表的下面(该表位于栈顶的 -1 处)。

您可能已经看到了这里要做什么:现在我们想修改新表,因此我们在正确的位置添加堆栈平衡的代码(在新表被推入堆栈后)。 我将跳过一些步骤,并通过缩进表示我插入代码的位置:

lua_newtable( L );  /* ==> stack: ..., {} */
{
  lua_pushliteral( L, "b" ); /* ==> stack: ..., {}, "b" */
  lua_newtable( L ); /* ==> stack: ..., {}, "b", {} */
  {
    lua_pushliteral( L, "c" ); /* == stack: ..., {}, "b", {}, "c" */
    lua_newtable( L ); /* ==> stack: ..., {}, "b", {}, "c", {} */
    {
      lua_pushliteral( L, "d" );
      lua_newtable( L );
      {
        lua_pushliteral( L, "e" );
        lua_pushliteral( L, "GOOD" );
        lua_settable( L, -3 );
      }
      lua_settable( L, -3 );
    }
    lua_settable( L, -3 ); */ ==> stack: ..., {}, "b", {} */
  }
  lua_settable( L, -3 ); /* ==> stack: ..., {} */
}
lua_setglobal( L, "a" ); /* ==> stack: ... */

当你正在开发具有复杂堆栈操作的代码时,通常在关键点打印出当前堆栈内容或至少检查堆栈上元素数量(参见lua_gettop())是否符合预期会有所帮助。 这里是我用来做这件事的工具。


谢谢回复。也许你知道如何将名称为“d”的最后一个表添加到现有表中,而不是使用键“e”和值“GOOD”创建新表? - BORSHEVIK
lua_newtable(L); 将一个值推入 Lua 栈。您可以将此语句替换为一系列语句,总共将相同数量的值推送到 Lua 栈上。这些语句取决于您现有表存储的位置(可能涉及 lua_getglobal())。 - siffiejoe
当然。在第三个代码片段中(等同于a = { b = {} }的那个),将第二个lua_newtable(L);替换为lua_getglobal(L, "myTable");。这将使其等同于a = { b = myTable } }lua_newtable()lua_getgobal()具有相同的堆栈效果,因此周围的代码可以保持不变。 - siffiejoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接