Lua arrays are tables whose keys happen to be consecutive integers. The C API provides lua_geti and lua_seti for this common case:
1 | int type = lua_geti(L, table_index, key); /* pushes table[key] */ |
Before Lua 5.3, use lua_rawgeti and lua_rawseti. Their raw access bypasses metamethods.
Mapping an array
The function below receives a table and a Lua function, applies the function to each array element, and stores the result back in place:
1 | static int l_map(lua_State *L) { |
In production code, consider lua_pcall when an error should be handled rather than propagated through the C stack.
Working with strings
Lua strings may contain zero bytes, so use length-aware functions such as luaL_checklstring instead of assuming C-style termination. To build a small result, push the fragments and call lua_concat. For larger or incremental output, use luaL_Buffer:
1 | static int l_upper_ascii(lua_State *L) { |
This example intentionally handles ASCII only; Unicode case conversion requires a Unicode-aware library.
Preserving C-side state
The registry is a Lua table reserved for C code. Store values under a unique light-userdata key when several functions need shared state. luaL_ref is useful when an integer reference is more convenient, and luaL_unref releases it.
For state owned by one exported function, a C closure is usually cleaner:
1 | static int counter(lua_State *L) { |
Each closure receives its own upvalue. Push the same table as an upvalue for multiple closures when they deliberately need shared mutable state.
The key discipline in every C function is stack accounting: document what each operation pushes or pops, validate arguments with luaL_check*, and return the exact number of results left on the stack.