Embedding Lua into executables using LuaJIT/FFI

> Coding, hacking, computer graphics, game dev, and such...
User avatar
fips
Site Admin
Posts: 166
Joined: Wed Nov 12, 2008 9:49 pm
Location: Prague
Contact:

Embedding Lua into executables using LuaJIT/FFI

Post by fips »

I've been recently doing a bit of research on various Lua/C binding possibilities and came across LuaJIT with its pretty nice FFI library. I've been immediately impressed by how simple it is to expose C functions and datatypes directly to Lua without the need of writing a single line of C code or using an external binding tool (it's similar to Python's ctypes to some degree). The library basically parses plane C declarations provided as a string and than does the binding automatically by looking up the symbols exported by a mapped binary. The best thing is that the symbols don't need to come from a DLL, but even a regular executable (exe) can export them! That's what makes FFI perfect for embedding Lua directly into an executable. There's no need for additional wrapping DLLs! This makes the whole setup simpler and enables additional optimizations and inlining.

The code below demonstrates the technique:

Code: Select all

/*
(c) 2012 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=589
*/

extern "C" {

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

} // extern "C"

#include <cassert>

// Please note that despite the fact that we build this code as a regular
// executable (exe), we still use __declspec(dllexport) to export
// symbols. Without doing that FFI wouldn't be able to locate them!

extern "C" __declspec(dllexport) void __cdecl hello_from_lua(const char *msg)
{
    printf("A message from LUA: %s\n", msg);
}

const char *lua_code =
"local ffi = require('ffi')                   \n"
"ffi.cdef[[                                   \n"
"const char * hello_from_lua(const char *);   \n" // matches the C prototype
"]]                                           \n"
"ffi.C.hello_from_lua('Hello from LUA!')      \n" // do actual C call
;

int main()
{
    lua_State *lua = luaL_newstate();
    assert(lua);
    luaL_openlibs(lua);

    const int status = luaL_dostring(lua, lua_code);
    if(status)
        printf("Couldn't execute LUA code: %s\n", lua_tostring(lua, -1));

    lua_close(lua);
    return 0;
}

// output:
// A message from LUA: Hello from LUA!