如何使用C API迭代表格中的项目

我正在为游戏引擎制作材料系统,并且正在尝试使用Lua脚本作为材料的配置文件。因此,一个示例材料文件可能如下所示:

material = {
    color = {0.3,1,0.2}
    specular_intensity = 0.3
    shininess = 0.1
}

但是,表格中的值将因着色器而异 - 因此,我想迭代表格中的值,然后尝试将它们的值设置为着色器中的uniform变量,这意味着我需要名称以及值。我该如何实现此功能(我在使用C API)?

点赞
用户2047576
用户2047576

你可以使用lua_next来完成这个操作。当你将nil压入 Lua 栈并在 while 循环中调用lua_next时,你将能够获取表中的每个键值对。

在每次成功调用lua_next之后,值将位于堆栈的顶部,而键则位于其下方。如果你想要继续遍历所有键值对,则必须从堆栈中弹出值但保留键。这样,lua_next就能确定下一个要访问的键。

你也可以通过从堆栈中弹出键和值并终止 while 循环来中断迭代。

下面是一个短例子,匹配了你问题中的表。

void log_color(lua_State* L, const std::string& key) {
    auto table_idx = lua_absindex(L, -1);
    if (lua_type(L, table_idx) != LUA_TTABLE || lua_rawlen(L, table_idx) != 3) {
        lua_pop(L, 2); /* 弹出表和键 */
        luaL_error(L, "key %s 需要一个包含 3 个数字值的表", key.c_str());
    }

    auto ttr = lua_rawgeti(L, table_idx, 1);
    auto ttg = lua_rawgeti(L, table_idx, 2);
    auto ttb = lua_rawgeti(L, table_idx, 3);

    if (ttr != LUA_TNUMBER || ttg != LUA_TNUMBER || ttb != LUA_TNUMBER) {
        lua_pop(L, 5); /*弹出 3 个颜色值、表和键*/
        luaL_error(L, "key %s 的值都应该是数值类型", key.c_str());
    }

    std::cout << "找到键 " << key << ",RGB 值为 " << lua_tonumber(L, -3) << "," << lua_tonumber(L, -2) << "," << lua_tonumber(L, -1) << "\n";
    lua_pop(L, 3);
}

int myMaterialFunction(lua_State* L) {
    std::string key;

    luaL_checktype(L, 1, LUA_TTABLE);

    // 为 lua_next 推入 nil,表示它需要选择第一个键
    lua_pushnil(L);

    while (lua_next(L, 1)) {
        // 键位于堆栈的 -2 位置,值位于 -1 位置。我们需要弹出值,
        // 但保留键在堆栈中,以便 lua_next 知道从哪里开始继续。在此时,您可以对它们进行任何处理。

        // 例如,当键不是字符串时,忽略它;
        if (lua_type(L, -2) != LUA_TSTRING) {
            lua_pop(L, 1); /* 弹出值 */
            continue;
        }

        // 如果它的键是字符串,就开始处理值;
        key = lua_tostring(L, -2);
        if (key == "color") {
            log_color(L, key); /* 处理一个颜色表 */
        } else if (key == "specular_intensity" || key == "shininess") {
            // 处理数值
            if (lua_type(L, -1) != LUA_TNUMBER) {
                lua_pop(L, 2);
                luaL_error(L, "key %s 需要一个数值类型的值", key.c_str());
            } else {
                std::cout << "找到键 " << key << ",值为 " << lua_tonumber(L, -1) << "\n";
            }
        }

        // 完成对值的处理后,弹出值
        lua_pop(L, 1);
    }

    return 0;
}
2020-10-24 10:19:01