如何从C++验证Lua表键

我试图使用 Lua 配置 C++ 应用程序,并且在配置出现问题时生成有用的消息,而不是 Lua 语法问题。

例如,假设以下是有效的配置:

foo = { a = 0, b = 'bar' }

但是用户实际上输入了以下内容:

foo = { a = 0, c = 'bar' }

现在,应用程序知道 foo 可以有字段 a 和 b。它可以加载 foo 并获得 a 的值。它甚至可以判断 b 没有设置,并使用默认设置。但是我想检测到 c 存在并报告警告。

以下是我的尝试的提取片段,它会失败:

static void check_table(lua_State* L)
{
    lua_pushnil(L);

    while ( lua_next(L, -2) )
    {
        // key at -2 and value at -1
        if ( lua_isstring(L, -2) )
        {
            const char* key = lua_tostring(L, -2);
            // validate here; just printing key for now
            cout << key << endl;
        }
        lua_pop(L, 1);
    }
}

只要表格不是实际数组,这个程序就能正常工作。当我遇到其中一个时,它在第二次迭代时会出错,错误如下:

...
1
PANIC: unprotected error in call to Lua API (invalid key to 'next')

我认为这是来自 Lua 参考页面的原因:

如果值是数字,则 lua_tolstring 还将堆栈中的实际值更改为字符串。 (当在表遍历期间将 lua_tolstring 应用于键时,此更改会混淆 lua_next。)“

有任何方法可以解决这个问题吗?我打开备选方案。理想情况下,会发出以下消息:

警告:conf.lua 第18行:表格 foo 不使用键'c',已忽略

(Lua 调试 API 也不提供文件名和行号,但这是另一个话题。)

PS:我知道 c 可能是无害的,但也可能是打字错误。在大型配置中,忽略这些事情可能会导致数个小时的头痛。

点赞
用户107090
用户107090

如果使用 Lua 编写,验证可能会更容易。我想像下面这样:

local template = { a="number", b="string"}

local function validate(t)
    for k,v in pairs(t) do
        if template[k]==nil then
            print("字段 "..k.." 不能存在")
        elseif type(v)~=template[k] then
             print("字段 "..k.." 应该是 "..template[k])
        end
    end
end

validate{ a = 0, b = 'bar' }
validate{ a = 0, b = 42 }
validate{ a = 0 }
validate{ a = 0, c = 'bar' }
2014-02-27 14:15:44
用户7932836
用户7932836

lua_isstring 定义如下:

LUA_API int lua_isstring (lua_State *L, int idx) {
    int t = lua_type(L, idx);
    return (t == LUA_TSTRING || t == LUA_TNUMBER);
}

所以应该使用以下代码替换:

if ( lua_type(L, -2) == LUA_TSTRING )
2017-04-27 17:33:57