简体   繁体   中英

When writing a Lua-facing function in C, what's a good way to check if an argument supports table-like lookups?

Here's a potential pattern that can check if an argument is a table:

int my_fn(lua_State *L) {
  luaL_checktype(L, 1, LUA_TTABLE);
  // .. do stuff with the table ..
}

This works whenever the first argument is a table. However, other Lua types support table lookups, such as a userdata, and in luajit, a cdata.

Is there a nice way to check if a table lookup, such as via lua_getfield , will succeed before I call it? I mean without restricting the type to tables. Relatedly, are tables, userdata, and cdata the only types in luajit that support indexed lookups?

I'm most interested in answers restricted to the Lua 5.1 C API because I'm using LuaJIT which currently works with this version.

Clarification

The advantage of the luaL_checkXXX functions is that, in one line, they:

  • throw an informative, user-friendly error message if the type is wrong, and
  • provide a C-friendly return value that can be used immediately.

I'm looking for something similar for tables. I don't expect a C-friendly hash-table return value, but I do want the same quality of error message to the user if the argument in question is not indexable.

I'm embracing the philosophy of duck typing. If I write a function that simply wants to index some keys from an argument, then I don't care if that argument is truly a table, or just a userdata that supports __index lookups. I want to accept either one.

In general, just tables have lookups because it's the only type which defines this property. Userdata are opaque, just the host knows what to do with it or adds a metatable (which can be analyzed) for specific behaviour. CData are part of Lua compiling with LuaJIT, i never used this type with C API (is it even supported?). At the end you have to check the type/metatable for possible lookups and request a field to check for setting, there's no way around lua_getfield (but raw access should be faster, see lua_rawget ). The exception would be to check for table array length by lua_objlen .

Furthermore a cheaper solution for type checking would be lua_is*** functions.

Here's one way to do it:

// If the value at index narg is not indexable, this function does not return and
// provides a user-friendly error message; otherwise the stack is unchanged.
static void luaL_checkindexable(lua_State *L, int narg) {
  if (lua_istable(L, narg)) return;  // tables are indexable.
  if (!luaL_getmetafield(L, narg, "__index")) {
    // This function will show the user narg and the Lua-visible function name.
    luaL_argerror(L, narg, "expected an indexable value such as a table");
  }
  lua_pop(L, 1);  // Pop the value of getmetable(narg).__index.
}

This works for tables and any value with an __index value on its metatable.

It provides a standard-format error given by luaL_argerror . Here's an example error message:

a_file.lua:7: bad argument #1 to 'fn' (expected an indexable value such as a table)

You can use it like this:

// This Lua-facing function expects an indexable 1st argument.
int my_fn(lua_State *L) {
  luaL_checkindexable(L, 1);
  lua_getfield(L, 1, "key");  // --> arg1.key or nil is now on top of stack.
  // .. your fn ..
}

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