简体   繁体   中英

How to validate Lua table keys from C++

I am trying to use Lua for the configuration of a C++ application and am having trouble generating helpful messages when something is wrong in the configuration, not the the Lua syntax.

For example, suppose the following is a valid configuration:

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

but the user actually typed this:

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

Now, the app knows that foo can have fields a and b. It can load foo and get the value of a. It can even tell that b is not set and use a default. But I want to detect that c is present and report a warning.

Here is an extract of my attempt at that which blows up:

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);
    }
}

This works fine as long as the table is not actually an array. When I hit one of those, it dies on the second iteration with this:

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

which I attribute to this from the Lua reference page:

"If the value is a number, then lua_tolstring also changes the actual value in the
 stack to a string. (This change confuses lua_next when lua_tolstring is applied to
 keys during a table traversal.)"

Any way around this? I am open to alternate approaches. Ideally a message could be emitted like:

WARNING: conf.lua line 18: table foo does not use key 'c', ignored

(The Lua debug API doesn't give the file name and line number either, but that is a different topic.)

PS: I know, c could benign, but it could also be a typo. In a large configuration, ignoring such things could lead to hours of head scratching.

Validation will probably be much easier if written in Lua. I have something like this in mind:

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

local function validate(t)
    for k,v in pairs(t) do
        if template[k]==nil then
            print("field "..k.." cannot be present")
        elseif type(v)~=template[k] then
             print("field "..k.." should be a "..template[k])
        end
    end
end

validate{ a = 0, b = 'bar' }
validate{ a = 0, b = 42 }
validate{ a = 0 }
validate{ a = 0, c = 'bar' }
lua_isstring is defined:
LUA_API int lua_isstring (lua_State *L, int idx) {
int t = lua_type(L, idx);
return (t == LUA_TSTRING || t == LUA_TNUMBER);
}

So instead of:

if ( lua_isstring(L, -2) )

use:

if ( lua_type(L, -2) == LUA_TSTRING )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM