传递一个 Vector<struct> 到 Lua 表中

我想通过发送一个 C++ 预格式化的 Lua 表来改进我的下面的代码:

int GetCategory(lua_State* L)
{
    uint32 Type = CHECKVAL<int>(L, 1);
    lua_newtable(L);
    int tbl = lua_gettop(L);
    uint32 counter = 1;
    // Struct CT { string CategoryBrandName, CategoryName }; > Vector<CT>
    auto list = sManagerMgr->GetAll();

    // Hack modify this to send a metatable/UserData/Table whatever is called
    for (auto& elem : list)
    {
        switch (Type)
        {
        case 1:
            lua_pushstring(L, elem->CategoryBrandName);
            break;
        case 2:
            lua_pushstring(L, elem->CategoryName);
            break;
        }
        lua_rawseti(L, tbl, counter);
        counter++;
    }

    lua_settop(L, tbl);
    return 1;
}

基本上, lua_newtable 将一个表推送到 Lua 栈中, lua_gettop 将取出顶部索引,因此我们在得到的 tbl 索引处放置表。 然后 lua_pushstring(L, ELEMENT); lua_rawseti(L, tbl, counter); 将 ELEMENT 放入索引为 tbl 的表中。元素的索引是计数器的值。

但问题在于,我被迫调用 GetCategory 函数两次才能填充它,如下所示:

table.insert(Group, { GetCategory(1), GetCategory(2) });

当前用法:

print(i, Group(1)[i], Group(2)[i]);

所以…我更愿意调用它一次并直接获得以下内容:

local Group =
{
        [1] = { "elem->CategoryBrandName[1]", "elem->CategoryName[1]" },
        [2] = { "elem->CategoryBrandName[2]", "elem->CategoryName[2]" }
        //etc
};

我尝试将 elem 填充到一个 2D 数组 [1][2] 中,然后不成功地将其推入数组中

我对表、元表、多维数组等进行了大量的研究,但找不到适合我的需要或可行的东西。

有人有解决办法吗?

点赞
用户4984564
用户4984564

为什么不让函数返回两个值呢?那样你就可以这么写:

local Group = { GetCategories }

我不是 C API 的专家,但我认为这可以通过调用 lua_newtable(L) 来轻松实现,就像这样:

int GetCategories(lua_State* L) {
  lua_settop(L, 0);
  // 抛弃参数,这样我们就不必保存栈顶,
  // 可以直接使用数字(见下面的代码)
  lua_newtable(L); // 栈顶索引为 1
  lua_newtable(L); // 栈顶索引为 2

  // 干你的事情

  lua_settop(L, 2); // 清除你的临时变量
  return 2; // 我们在 Lua 中返回的值的数量
}

优化提示:你可以使用 lua_createtable 并告诉它每个表将有多少个元素,这样 Lua 就可以为其预分配一些内存。

编辑:我刚刚注意到你的代码中:

for (auto& elem : list) {
  switch (Type) {
  case 1:
    lua_pushstring(L, elem->CategoryBrandName);
    break;
  case 2:
    lua_pushstring(L, elem->CategoryName);
    break;
  }
  lua_rawseti(L, tbl, counter);
  counter++;
}

你不断地往堆栈里推值。对于长向量,它可能很快就会导致堆栈溢出,从而带来麻烦。更好的方法是:1)推到堆栈,2)插入到表中,3)把它们弹出来:

// 修改为我建议的返回两个表的实现方式。
// 这里它们可以很容易地相互转换。
for (auto& elem : list) {
  lua_pushstring(L, elem->CategoryBrandName);
  lua_rawseti(L, 1, counter++);
  lua_pop(L, 1);

  lua_pushstring(L, elem->CategoryName);
  lua_rawseti(L, 2, counter++);
  lua_pop(L, 1);
}

时刻牢记哪些东西在堆栈上是个好主意。节省一些内存不仅可以提高性能,还可以避免由于(Lua)堆栈溢出而带来的潜在问题。

最后一个细节:Lua 中不需要 ;,除非你在同一行中有两个语句 print('more readable'); print('like this'),否则使用它们被认为是一种不好的风格。

2018-08-24 07:17:52
用户7270457
用户7270457

如果有人正在寻找类似的东西,这里是我使用lua_createtable的方式。它可以按预期工作,但可能需要一些改进。

int GetCategory(lua_State* L)
    {
        int counter = 1;
        int MaxListSize = 2;
        auto Categories = sManagerMgr->GetAll();

        lua_createtable(L, Categories.size(), 0);

        for (auto& elem : Categories)
        {
            vector<string> list;
            list.reserve(MaxListSize);

            list.emplace_back(elem->CategoryBrandName);
            list.emplace_back(elem->CategoryName);

            Macro::Push(L, counter); // custom
            lua_createtable(L, 0, MaxListSize);

            for (int i = 1; i <= MaxListSize; i++)
            {
                Macro::Push(L, list.at(i - 1)); // custom
                lua_rawseti(L, -2, i);
            }

            lua_settable(L, -3);
            list.clear();
            counter++;
        }
        return 1;
    }

将会产生类似以下的输出

local Group =
{
        [1] = { "elem->CategoryBrandName[1]", "elem->CategoryName[2]" },
        [2] = { "elem->CategoryBrandName[1]", "elem->CategoryName[2]" }
        --等等
};
2018-08-28 16:11:03