Embedding is Lua's core purpose: every host from Redis to game engines uses this API.
Lua is a C library first. A host creates an interpreter with luaL_newstate(), opens the standard libraries with luaL_openlibs, runs code with luaL_dostring or luaL_loadfile plus lua_pcall, and destroys it with lua_close. Each lua_State is fully independent, so a host can run many sandboxes.
All data exchange goes through a virtual stack. You push values (lua_pushinteger, lua_pushstring), read them by index (lua_tointeger(L, -1); positive indexes count from the bottom, negative from the top) and pop them with lua_pop. Lua's garbage collector owns the values; the stack protects them from being collected while you use them.
A C function callable from Lua has the signature int f(lua_State *L): it reads its arguments from the stack (luaL_checkinteger(L, 1) validates and raises a Lua error on bad input), pushes its results and returns how many it pushed. Register it with lua_register or, for a module, a luaL_Reg array and luaL_newlib inside a luaopen_name function that require finds via package.cpath.
Lua errors use longjmp, so a C function that raises never returns; always run Lua code with lua_pcall at the top level. Keep references to Lua values beyond a call in the registry (luaL_ref), and use full userdata with a metatable to wrap C structs.
Example
// cc host.c -llua5.4 -I/usr/include/lua5.4 && ./a.out
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
static int c_add(lua_State *L) {
lua_Integer a = luaL_checkinteger(L, 1);
lua_Integer b = luaL_checkinteger(L, 2);
lua_pushinteger(L, a + b);
return 1; /* one result */
}
int main(void) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_register(L, "add", c_add);
if (luaL_dostring(L, "return add(2, 40) * 2") != LUA_OK)
fprintf(stderr, "%s\n", lua_tostring(L, -1));
else
printf("%lld\n", (long long)lua_tointeger(L, -1)); /* 84 */
luaL_dostring(L, "add('x', 1)");
printf("%s\n", lua_tostring(L, -1)); /* ...bad argument #1 to 'add'... */
lua_close(L);
return 0;
}Remember
- All values cross between C and Lua via the virtual stack
- A C function returns the number of results it pushed
- Run Lua code with
lua_pcall/luaL_dostring; errors longjmp
Watch out for
- Leaving values on the stack in loops, which grows it until it overflows.