This is going to be a little different to my usual rants about build systems.

Codethink does friday talks about varying topics, so people get practise speaking, and knowledge is shared. My talk is going to be about embedding the Lua interpreter in a C program.

Starting at hello

We need somewhere to start, so here's a hello world, including a makefile.

Here's hello.c

#include <stdio.h>

int main(void){
    printf("Hello World!\n");
    return 0;
}

Here's the Makefile

CC = gcc
OBJS = hello.o
BINS = hello

all: $(BINS)

hello: $(OBJS)

.PHONY: clean
clean:
    $(RM) $(OBJS) $(BINS)

Install your distribution's build-essential and run make to build it. Run it as follows:

$ ./hello
Hello World!

Embedding Lua

Build-system changes

I'm going to use luajit, it's API and ABI compatible with lua 5.1, but faster. To install the required libraries in Ubuntu 12.04 run sudo apt-get install luajit libluajit-5.1-2 libluajit-5.1-dev.

To let the built executable use luajit, add the following to the Makefile.

CFLAGS = `pkg-config --cflags luajit`
LDLIBS = `pkg-config --libs-only-l luajit`
LDFLAGS = `pkg-config --libs-only-L luajit`

Adding CFLAGS lets you include the headers, LDLIBS adds the libraries to the linker command, LDFLAGS lets the linker find the libraries.

hello.c changes

I'm not as familiar with the C api as I am with the lua language itself and its standard libraries, so I refer to The Lua 5.1 Reference manual.

Given the nature of the changes, hello.c is almost entirely rewritten, so I'm going to show the new version commented inline.

#include <stdio.h>

/* lua.h provides core functions, these include everything starting lua_ */
#include <lua.h>
/* lauxlib.h provides convenience functions, these start luaL_ */
#include <lauxlib.h>

int main(void){
    /* The lua_State structure encapsulates your lua runtime environment
       luaL_newstate allocates and initializes it.
       There also exists a lua_newstate, which takes parameters for a custom
       memory allocator, since the lua runtime needs to allocate memory.
       luaL_newstate is a wrapper, which uses the standard C library's realloc,
       and aborts on allocation failure.
     */
    lua_State *L = luaL_newstate();
    if (!L) {
        perror("Creating lua state");
        return 1;
    }

    /* The following executes the file hello.lua, which returns the string
       intended to be printed.
       Likewise it is an auxiliary function, which reads a file,
       compiles the contents into a function, then calls it.
     */
    if (luaL_dofile(L, "hello.lua")) {
        /* dofile returns a string on the lua stack in the case of error
           which says what went wrong, a pointer to it is retrieved with
           lua_tostring, this exists inside the lua_State structure, so
           if you need it after the next call to lua you have to copy it
         */
        char const *errmsg = lua_tostring(L, 1);
        fprintf(stderr, "Failed to execute hello.lua: %s\n", errmsg);
        return 2;
    }

    /* A file can take parameters and return values like a function.
       The result is passed to printf, so IO is done outside of lua.
     */
    char const *result = lua_tostring(L, 1);
    if (!result) {
        fprintf(stderr, "hello.lua did not return a string\n");
        return 3;
    }
    printf("%s\n", result);

    /* lua_close frees up any resources used by the lua runtime
       This is not necessary here, since the program is about to exit.
     */
    lua_close(L);
    return 0;
}

hello.lua

To completely reproduce behaviour before we over-complicated "Hello World", write return "Hello World!" to hello.lua.

$ echo 'return "Hello World!"' >hello.lua
$ ./hello
Hello World!

Right now this is kind of pointless, however lua is a complete programming language, so you can get the sum of numbers 1 to 10 do the following.

$ cat >hello.lua <<'EOF'
local sum = 0
for i=1, 10 do
    sum = sum + i
end
return sum
EOF
$ ./hello
55

Links

The source code for this is available at git://git.gitano.org.uk/personal/richardmaw/lua/binding-lua-from-c.git. The different steps are tagged.