Nanfeng

Notes on software development, code, and curious ideas

Calling C from Lua: Functions, Registration, and Modules

Lua cannot call an arbitrary C function directly. A callable function must follow the lua_CFunction convention:

1
typedef int (*lua_CFunction)(lua_State *L);

Arguments are available on a private stack starting at index 1. The function pushes its results and returns their count. Each invocation gets its own stack frame, including recursive calls.

A small C function

1
2
3
4
5
6
7
8
9
#include <math.h>
#include <lua.h>
#include <lauxlib.h>

static int l_sin(lua_State *L) {
lua_Number value = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(value));
return 1;
}

luaL_checknumber both validates and converts the argument. It produces a normal Lua error when the value is invalid. Optional arguments can use helpers such as luaL_optnumber.

Registering the function

An embedding application can expose the function as a global:

1
2
lua_pushcfunction(L, l_sin);
lua_setglobal(L, "mysin");

Lua can then call mysin(math.pi / 2). Libraries should normally avoid adding globals and return a module table instead.

Building a module

1
2
3
4
5
6
7
8
9
static const luaL_Reg mathx[] = {
{"sin", l_sin},
{NULL, NULL}
};

int luaopen_mathx(lua_State *L) {
luaL_newlib(L, mathx);
return 1;
}

Compile this as a shared library named for the platform, place it on package.cpath, and load it with:

1
2
local mathx = require("mathx")
print(mathx.sin(math.pi / 2))

The exported loader must be named luaopen_<module>. Exact compiler and linker flags depend on the Lua version and operating system; compile and link against the same Lua ABI used by the host application.

Errors and continuations

Use luaL_error for an invalid operation in a library function. When C calls back into Lua, prefer lua_pcall if the host needs to recover from errors. Yielding across C boundaries requires the continuation APIs, such as lua_yieldk and lua_pcallk; a plain C call frame cannot always be resumed safely.

The central rule is simple: treat the Lua stack as the function’s calling convention, validate every argument, and make ownership of every pushed value explicit.

+