| name | Lua C Integration |
| user-invocable | false |
| description | Use when lua C API for extending Lua with native code including stack operations, calling C from Lua, calling Lua from C, creating C modules, userdata types, metatables in C, and performance optimization techniques. |
| allowed-tools | [] |
Lua C Integration
Introduction
Lua's C API enables seamless integration with C code, allowing developers to
extend Lua with high-performance native functionality or embed Lua as a scripting
engine within C applications. This bidirectional integration makes Lua ideal for
performance-critical applications requiring scripting capabilities.
The C API operates through a virtual stack for passing values between Lua and C,
with functions for manipulating Lua values, calling functions, and managing
memory. Understanding stack operations and Lua's data model is essential for
safe, efficient C integration.
This skill covers the Lua stack, calling C from Lua, calling Lua from C,
creating C modules, userdata and metatables, error handling, memory management,
and performance optimization patterns.
Lua Stack Fundamentals
The Lua-C API uses a virtual stack for all value exchange between Lua and C,
requiring understanding of push/pop operations.
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
void demonstrate_stack(lua_State *L) {
lua_pushinteger(L, 42);
lua_pushnumber(L, 3.14);
lua_pushstring(L, "hello");
lua_pushboolean(L, 1);
lua_pushnil(L);
int top = lua_gettop(L);
lua_Integer i = lua_tointeger(L, 1);
lua_Number n = lua_tonumber(L, 2);
const char *s = lua_tostring(L, 3);
int b = lua_toboolean(L, 4);
lua_Number n2 = lua_tonumber(L, -4);
const char *s2 = lua_tostring(L, -3);
if (lua_isnumber(L, 1)) {
}
if (lua_isstring(L, 3)) {
}
lua_pop(L, 1);
lua_remove(L, 2);
lua_pushstring(L, "world");
lua_replace(L, 3);
lua_settop(L, 0);
}
void stack_patterns(lua_State *L) {
lua_pushstring(L, "new value");
lua_insert(L, 1);
lua_pushvalue(L, 1);
lua_rotate(L, 1, 2);
if (!lua_checkstack(L, 100)) {
}
int abs_idx = lua_absindex(L, -1);
}
int check_arguments(lua_State *L) {
int argc = lua_gettop(L);
if (argc < 2) {
return luaL_error(L, "Expected at least 2 arguments");
}
if (!lua_isnumber(L, 1)) {
return luaL_error(L, "Argument 1 must be a number");
}
if (!lua_isstring(L, 2)) {
return luaL_error(L, "Argument 2 must be a string");
}
return 0;
}
void table_operations(lua_State *L) {
lua_newtable(L);
lua_pushstring(L, "value");
lua_setfield(L, -2, "key");
lua_getfield(L, -1, "key");
const char *value = lua_tostring(L, -1);
lua_pop(L, 1);
lua_pushstring(L, "key2");
lua_pushinteger(L, 42);
lua_settable(L, -3);
lua_pushinteger(L, 1);
lua_pushstring(L, "first");
lua_settable(L, -3);
lua_pushstring(L, "rawkey");
lua_pushstring(L, "rawvalue");
lua_rawset(L, -3);
lua_len(L, -1);
lua_Integer len = lua_tointeger(L, -1);
lua_pop(L, 1);
}
void global_operations(lua_State *L) {
lua_pushinteger(L, 42);
lua_setglobal(L, "my_global");
lua_getglobal(L, "my_global");
lua_Integer value = lua_tointeger(L, -1);
lua_pop(L, 1);
}
Master stack operations for efficient C-Lua value exchange and avoid stack
overflow through proper cleanup.
Calling C Functions from Lua
C functions follow specific signatures and conventions to be callable from Lua
scripts.
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
static int add(lua_State *L) {
lua_Number a = luaL_checknumber(L, 1);
lua_Number b = luaL_checknumber(L, 2);
lua_Number result = a + b;
lua_pushnumber(L, result);
return 1;
}
static int divide_with_remainder(lua_State *L) {
lua_Integer a = luaL_checkinteger(L, 1);
lua_Integer b = luaL_checkinteger(L, 2);
if (b == 0) {
return luaL_error(L, "Division by zero");
}
lua_pushinteger(L, a / b);
lua_pushinteger(L, a % b);
return 2;
}
static int greet(lua_State *L) {
const char *name = luaL_optstring(L, , );
lua_pushfstring(L, , name);
;
}
{
luaL_checktype(L, , LUA_TTABLE);
lua_Number sum = ;
lua_Integer len = luaL_len(L, );
(lua_Integer i = ; i <= len; i++) {
lua_geti(L, , i);
sum += lua_tonumber(L, );
lua_pop(L, );
}
lua_pushnumber(L, sum);
;
}
{
lua_Number x = luaL_checknumber(L, );
lua_Number y = luaL_checknumber(L, );
lua_newtable(L);
lua_pushnumber(L, x);
lua_setfield(L, , );
lua_pushnumber(L, y);
lua_setfield(L, , );
;
}
{
n = lua_gettop(L);
( i = ; i <= n; i++) {
*str = luaL_tolstring(L, i, );
(, str);
lua_pop(L, );
}
;
}
{
luaL_checktype(L, , LUA_TTABLE);
luaL_checktype(L, , LUA_TFUNCTION);
lua_Integer len = luaL_len(L, );
(lua_Integer i = ; i <= len; i++) {
lua_pushvalue(L, );
lua_geti(L, , i);
lua_pushinteger(L, i);
(lua_pcall(L, , , ) != LUA_OK) {
lua_error(L);
}
}
;
}
luaL_Reg mylib[] = {
{, add},
{, divide_with_remainder},
{, greet},
{, sum_table},
{, create_point},
{, print_all},
{, each},
{, }
};
{
luaL_newlib(L, mylib);
;
}
{
lua_register(L, , add);
lua_register(L, , greet);
}
C functions must follow Lua's calling convention and properly manage the stack
for reliable integration.
Calling Lua from C
C code can load Lua scripts, call Lua functions, and access Lua global variables.
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
void execute_script(const char *filename) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if (luaL_dofile(L, filename) != LUA_OK) {
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
lua_close(L);
return;
}
lua_close(L);
}
void execute_string(const char *code) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if (luaL_dostring(L, code) != LUA_OK) {
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
}
lua_close(L);
}
void call_lua_function(lua_State *L, const char *func_name, int arg) {
lua_getglobal(L, func_name);
if (!lua_isfunction(L, -1)) {
fprintf(stderr, "%s is not a function\n", func_name);
lua_pop(L, 1);
;
}
lua_pushinteger(L, arg);
(lua_pcall(L, , , ) != LUA_OK) {
(, ,
func_name, lua_tostring(L, ));
lua_pop(L, );
;
}
lua_Integer result = lua_tointeger(L, );
(, result);
lua_pop(L, );
}
{
lua_getglobal(L, );
lua_pushinteger(L, );
lua_pushinteger(L, );
(lua_pcall(L, , , ) != LUA_OK) {
(, , lua_tostring(L, ));
;
}
lua_Integer quotient = lua_tointeger(L, );
lua_Integer remainder = lua_tointeger(L, );
lua_pop(L, );
(, quotient, remainder);
}
{
*msg = lua_tostring(L, );
luaL_traceback(L, L, msg, );
;
}
{
lua_pushcfunction(L, error_handler);
errfunc_idx = lua_gettop(L);
lua_getglobal(L, func_name);
lua_pushinteger(L, );
(lua_pcall(L, , , errfunc_idx) != LUA_OK) {
(, , lua_tostring(L, ));
lua_pop(L, );
} {
lua_pop(L, );
}
lua_pop(L, );
}
{
lua_getglobal(L, );
(!lua_istable(L, )) {
(, );
lua_pop(L, );
;
}
lua_getfield(L, , );
lua_Integer timeout = lua_tointeger(L, );
lua_pop(L, );
lua_pushnil(L);
(lua_next(L, ) != ) {
*key = lua_tostring(L, );
*value = lua_tostring(L, );
(, key, value);
lua_pop(L, );
}
lua_pop(L, );
}
{
lua_pushinteger(L, value);
lua_setglobal(L, name);
}
{
(luaL_loadstring(L, code) != LUA_OK) {
(, , lua_tostring(L, ));
lua_pop(L, );
;
}
(lua_pcall(L, , , ) != LUA_OK) {
(, , lua_tostring(L, ));
lua_pop(L, );
}
}
Calling Lua from C enables using Lua as a configuration or scripting layer
within C applications.
Creating C Modules
C modules package related functions and constants for use in Lua programs.
#include <lua.h>
#include <lauxlib.h>
#include <math.h>
static int vector_new(lua_State *L) {
lua_Number x = luaL_checknumber(L, 1);
lua_Number y = luaL_checknumber(L, 2);
lua_newtable(L);
lua_pushnumber(L, x);
lua_setfield(L, -2, "x");
lua_pushnumber(L, y);
lua_setfield(L, -2, "y");
return 1;
}
static int vector_add(lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checktype(L, 2, LUA_TTABLE);
lua_getfield(L, 1, "x");
lua_Number x1 = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "y");
lua_Number y1 = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "x");
lua_Number x2 = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "y");
lua_Number y2 = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_newtable(L);
lua_pushnumber(L, x1 + x2);
lua_setfield(L, -2, "x");
lua_pushnumber(L, y1 + y2);
lua_setfield(L, -2, );
;
}
{
luaL_checktype(L, , LUA_TTABLE);
lua_getfield(L, , );
lua_Number x = lua_tonumber(L, );
lua_pop(L, );
lua_getfield(L, , );
lua_Number y = lua_tonumber(L, );
lua_pop(L, );
lua_pushnumber(L, (x*x + y*y));
;
}
luaL_Reg vector_funcs[] = {
{, vector_new},
{, vector_add},
{, vector_magnitude},
{, }
};
{
luaL_newlib(L, vector_funcs);
lua_pushnumber(L, M_PI);
lua_setfield(L, , );
lua_pushnumber(L, M_E);
lua_setfield(L, , );
;
}
luaL_Reg math_basic[] = {
{, add},
{, subtract},
{, }
};
luaL_Reg math_trig[] = {
{, trig_sin},
{, trig_cos},
{, }
};
{
lua_newtable(L);
luaL_newlib(L, math_basic);
lua_setfield(L, , );
luaL_newlib(L, math_trig);
lua_setfield(L, , );
;
}
counter;
name[];
} ModuleState;
{
ModuleState *state = (ModuleState *)lua_touserdata(L, lua_upvalueindex());
lua_pushinteger(L, state->counter);
;
}
{
ModuleState *state = (ModuleState *)lua_touserdata(L, lua_upvalueindex());
state->counter++;
;
}
{
ModuleState *state = (ModuleState *)lua_newuserdata(L, (ModuleState));
state->counter = ;
(state->name, , (state->name));
lua_newtable(L);
lua_pushvalue(L, );
lua_pushcclosure(L, get_counter, );
lua_setfield(L, , );
lua_pushvalue(L, );
lua_pushcclosure(L, increment_counter, );
lua_setfield(L, , );
;
}
Organize related functionality into modules for clean API design and namespace
management.
Userdata and Metatables
Userdata wraps C structures for use in Lua with custom behavior through
metatables.
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
typedef struct {
double x;
double y;
} Point;
#define POINT_METATABLE "Point"
static int point_new(lua_State *L) {
double x = luaL_checknumber(L, 1);
double y = luaL_checknumber(L, 2);
Point *p = (Point *)lua_newuserdata(L, sizeof(Point));
p->x = x;
p->y = y;
luaL_getmetatable(L, POINT_METATABLE);
lua_setmetatable(L, -2);
return 1;
}
static Point *check_point(lua_State *L, int index) {
return (Point *)luaL_checkudata(L, index, POINT_METATABLE);
}
static int point_distance(lua_State *L) {
Point *p1 = check_point(L, 1);
Point *p2 = check_point(L, 2);
double dx = p2->x - p1->x;
double dy = p2->y - p1->y;
double dist = sqrt(dx*dx + dy*dy);
lua_pushnumber(L, dist);
return ;
}
{
Point *p = check_point(L, );
lua_pushfstring(L, , p->x, p->y);
;
}
{
Point *p1 = check_point(L, );
Point *p2 = check_point(L, );
Point *result = (Point *)lua_newuserdata(L, (Point));
result->x = p1->x + p2->x;
result->y = p1->y + p2->y;
luaL_getmetatable(L, POINT_METATABLE);
lua_setmetatable(L, );
;
}
{
Point *p1 = check_point(L, );
Point *p2 = check_point(L, );
lua_pushboolean(L, p1->x == p2->x && p1->y == p2->y);
;
}
{
Point *p = check_point(L, );
*key = luaL_checkstring(L, );
((key, ) == ) {
lua_pushnumber(L, p->x);
;
} ((key, ) == ) {
lua_pushnumber(L, p->y);
;
}
;
}
{
Point *p = check_point(L, );
*key = luaL_checkstring(L, );
value = luaL_checknumber(L, );
((key, ) == ) {
p->x = value;
} ((key, ) == ) {
p->y = value;
}
;
}
{
Point *p = check_point(L, );
;
}
luaL_Reg point_metamethods[] = {
{, point_tostring},
{, point_add},
{, point_eq},
{, point_index},
{, point_newindex},
{, point_gc},
{, }
};
{
luaL_newmetatable(L, POINT_METATABLE);
luaL_setfuncs(L, point_metamethods, );
lua_newtable(L);
lua_pushcfunction(L, point_new);
lua_setfield(L, , );
lua_pushcfunction(L, point_distance);
lua_setfield(L, , );
;
}
{
Point *p = (Point *)((Point));
p->x = ;
p->y = ;
lua_pushlightuserdata(L, p);
;
}
Userdata enables passing C structures to Lua while controlling access through
metatables.
Best Practices
-
Always check lua_pcall results to catch and handle Lua errors properly
-
Use luaL_check functions for argument validation with clear error messages
-
Balance push and pop operations to prevent stack overflow and leaks
-
Create metatables for userdata to enable natural Lua-style access patterns
-
Use luaL_newlib for modules to simplify registration of function tables
-
Handle errors with luaL_error rather than returning error codes
-
Avoid lua_tostring for numbers as it modifies the stack; use lua_tonumber
-
Use lua_absindex when stack positions change during operations
-
Register metamethods for userdata cleanup to prevent memory leaks
-
Document stack effects in comments for complex C functions
Common Pitfalls
-
Stack overflow from unbalanced push/pop causes crashes and undefined
behavior
-
Not checking function return types leads to type mismatches and errors
-
Using lua_tostring without checking modifies stack for non-strings
-
Calling lua_error directly instead of luaL_error loses error context
-
Accessing invalid stack indices causes undefined behavior and crashes
-
Not setting metatables on userdata makes garbage collection unreliable
-
Mixing absolute and relative indices causes confusion and bugs
-
Forgetting lua_pcall error handling causes uncaught exceptions
-
Not using luaL_checkudata allows type confusion and crashes
-
Closing lua_State prematurely invalidates all references and causes
crashes
When to Use This Skill
Apply C integration when performance-critical operations exceed pure Lua
capabilities.
Use C modules to wrap existing C libraries for use in Lua applications.
Leverage userdata when managing complex C structures from Lua code.
Embed Lua as a scripting engine in C applications requiring runtime
configuration.
Create C extensions for computationally intensive algorithms unsuitable for Lua.
Implement low-level system operations or hardware interfaces through C bindings.
Resources