luaswigtypemaps

How to detect if a table has a string key in SWIG typemap


I'm trying to create a SWIG typemap for a function that takes Lua table as an argument.

Here's my typemap which works fine with a table with number keys.

%typemap(in) (int argc, t_atom *argv)
{
    if (!lua_istable(L, $input))
        SWIG_exception(SWIG_RuntimeError, "argument mismatch: table expected");
    lua_len(L, $input);
    $1 = static_cast<lua_Integer>(lua_tointeger(L, -1));
    if (!$1) SWIG_exception(SWIG_RuntimeError, "table is empty");
    $2 = static_cast<t_atom *>(getbytes($1 * sizeof(t_atom)));
    for (int i = 0; i < $1; ++i) 
    {
        lua_pushinteger(L, i + 1);
        lua_gettable(L, $input);
        if (lua_isboolean(L, -1)) 
        {
            $2[i].a_type = A_FLOAT;
            $2[i].a_w.w_float = static_cast<t_float>(lua_toboolean(L, -1));
        }  
        else if (lua_isnumber(L, -1)) 
        {
            $2[i].a_type = A_FLOAT;
            $2[i].a_w.w_float = static_cast<t_float>(lua_tonumber(L, -1));
        }          
        else if (lua_isstring(L, -1)) 
        {
            $2[i].a_type = A_SYMBOL;
            $2[i].a_w.w_symbol = gensym(lua_tostring(L, -1));
        }
        else 
        {
            SWIG_exception(SWIG_RuntimeError, "unhandled argument type");
        }
    }
}
%typemap(freearg) (int argc, t_atom *argv) 
{
    freebytes($2, $1 * sizeof(t_atom));
}

However, when the function takes tables with a string key. It just prints SWIG_RuntimeError:table is empty.

I would like to detect if the table has a string key before it prints this error.

I tried the following condition but it didn't work:

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

How can I properly detect if a table argument has at least one string key in a SWIG typemap?


Solution

  • Once you know the length of the table is 0 (which means it has zero integer keys), you can fetch the next key-value pair with lua_next(). If it returns anything you know the tabel contains at least one non-integer key.

    /* table is in the stack at index 't',
     * and is known to have no integer keys
     */
    lua_pushnil(L);  /* first key */
    if (lua_next(L, t) != 0) {
        /* Table contains non-integer key */
        lua_pop(L, 2);  /* throw away key-value pair */
        /* Do stuff here */
    }
    

    They key can however be of type table or float or any other type that is alowed as a key, so if you need to check explicitly for the occurrence of a string key you will have to call lua_next() in a loop and check the type of each returned key until you find a string key or exhausts the loop.

    has_stringkey = false;
    /* table is in the stack at index 't' */
    lua_pushnil(L);  /* first key */
    while (lua_next(L, t)) {
        lua_pop(L, 1);  /* throw away value */
        if (lua_type(L, -1) == LUA_TSTRING) {
            has_stringkey = true;
            lua_pop(L, 1);  /* throw away key before exit loop */
            break;
        }
    }
    if (has_stringkey) { /* do stuff */ }