Play With Lua!

Tying C to Lua

without comments

I wanted to write something about Lua’s C interface, but I couldn’t think of anything good and self-contained with my Cocoa Lua stuff (part of the point of using notifications is that I don’t have to do much C interface stuff) so I figured, I’ll write a tiny tile-based map thing.

Let’s say you want to write a game in Lua, and you want to have a really gigantic map. Storing it as a Lua table would be a little unwieldy, especially if you want to do things like run functions over it for map generation (fractal map generation, smoothing, whatever). It would be really nice to write that in C and have some basic functions in Lua to access it. So, we’re going to write a function that creates a map of a certain size, and another one that returns (as a table) a rectangular “slice” of the map:

map = Map.create(256, 256)
Map.slice(map, 256, 100, 100, 15, 15) -- map_width, x, y, width, height

For our purposes, a map will just be a rectangular array of bytes, we’ll populate it with a really simple function.

Lua’s C interface is really simple and elegant, and works like no other embedded scripting language I’ve seen. Every time Lua calls into C, it creates a stack of Lua values. You can manipulate the stack with the usual stack-related verbs, then push your return values (Lua takes multiple-return seriously) back on to it. They don’t need to provide a C equivalent for every kind of Lua variable, nor do they need you to specify the signature of the functions you expose: they all take a Lua state (which contains the stack, among other things) and return an int (the number of things you returned to Lua).

So we register two functions like this:

static const struct luaL_reg map_lib[] = {
    {"create", map_create},
    {"slice", map_slice},
    {NULL, NULL}};

Null-terminated list of tuples containing a C function pointer and a Lua name for the function. Then, we call a convenience method to create a table to hold all these and register them:

luaL_openlib(L, "Map", map_lib, 0);

Now we can call Map.create and get to map_create. So let’s see how that all works:

Lua likes everything being 1-indexed. It’s a little annoying for some things but it’s really convenient when it’s used for addressing the stack. See, positive number indices count up from the bottom of the stack and negative ones count down from the top, so, your first argument is at index 1, the second at index 2, the top of the stack (last thing you pushed) is -1, and so on. What we’re going to do is pull the top two arguments off as numbers (with error-checking), create a block of memory that big, and push it back on to the stack as a light userdata (essentially a Lua wrapper around a C pointer; it doesn’t get any of Lua’s garbage-collection like a real userdata would though). Here’s how we do that:

int map_create(lua_State *lua){
    int w = luaL_checkinteger(lua, 1);
    int h = luaL_checkinteger(lua, 2);
 
    unsigned char *map = malloc(w * h);
 
    int n;
    for(n = 0; n != w * h; n++){
        map[n] = n % 10;
    }
 
    lua_pushlightuserdata(lua, map);
    return 1;
}

We set the map to a repeating pattern of digits so we can tell that it works when we print out a slice later. It’s pretty easy to see how this works.

The slice function is a little trickier; we’re going to create a Lua table and write a slice of the map to it:

int map_slice(lua_State *lua){
    unsigned char *map = lua_touserdata(lua, 1);
    int map_width = luaL_checkinteger(lua, 2);
    int x = luaL_checkinteger(lua, 3);
    int y = luaL_checkinteger(lua, 4);
    int w = luaL_checkinteger(lua, 5);
    int h = luaL_checkinteger(lua, 6);
 
    lua_newtable(lua);
 
    int cx, cy;
    for(cy = 0; cy != h; cy++)
        for(cx = 0; cx != w; cx++){
            lua_pushnumber(lua, cx + w * cy);
            lua_pushnumber(lua, map[x + cx + (y + cy) * map_width]);
            lua_settable(lua, -3);
        }
 
    return 1;
}

Setting a value in a table is a little unwieldy. The settable function expects the value on the top of the stack and the key right below it, then it takes a parameter giving the index of the table they’re going in to. If you push (or create) the table, then push the other two, the toble is at -3.

Once we have all that, we can call slice and get a table with 0..n keys containing the cells of our map slice. We can concat rows of the map together to print them out in a square:

map = Map.create(256, 256)
s = Map.slice(map, 256, 0, 0, 5, 5)
for i=0,4 do
    row = table.concat(s,' ',5*i, 5*i+4)
    print(row)
end

And everything is lovely!

So it’s actually a neat way to embed a scripting language in C. The stack has some limitations, but they make sense: you can get C numbers and strings out (and userdata) but you can only manipulate Lua functions in the stack, which means we never need to worry about a C program freeing something it shouldn’t. If you need to hold on to a Lua function across calls (like for a callback) you can store it in a special place in the stack.

If you want to see this code all together, it’s here.

Written by randrews

May 16th, 2011 at 8:13 pm

Posted in Uncategorized