"LuaJIT FFI Primer: No More Bindings"

Posted by Ryan C. Scott on Thu 29 May 2014

LuaJIT, which by itself is damned incredible, provides an interface called FFI that allows for directly interacting with C functions and structers. I've mentioned it before in my post about my particle language.

The FFI library allows calling external C functions and using C data structures from pure Lua code, obviating the need for explicit bindings that use the Lua stack, etc.

It parses plain C declarations! Mostly!

LuaJIT's site

Data Structure

Aside from the obvious interacting with libraries or your internal systems, FFI can be super convenient for working with structures. There are only a handful of primitive types in lua (e.g. number, string, table). Compound types are usually implemented, or more accurately simulated, by way of tables with key/value pairs or metatables. Using FFI you can quickly and cleanly throw together properly typed structures that will be both compatible with C functions and garbage collected.

Struct definitions are exactly as in C. Actual C, not C++. I mention it because if you spend enough time away from C it's easy to forget the nuances.

ffi.cdef [[
    typedef struct
    {
        int count;
        float scores[ 50 ];
    } ReallyUsefulStructure;
]]

local test = ffi.new( "ReallyUsefulStructure" );

for i = 0, 10 do
    test.scores[ i ] = i * 0.5;
    test.count = test.count + 1;
end

Should you put everything in such structs? Probably not. But structures like map graphs (with nodes, connections, etc.) are 100% easier to create this way.

Function Binding

Any exported function can be accessed from Lua.

extern "C" {
    __declspec( dllexport ) void Hello( const char *str )
    {
        printf( "Hello %s", str );
    }
}

And then in lua you can more or less cut and paste the function declaration and be on your way.

ffi.cdef [[
    void Hello( const char *str );
]]

ffi.C.Hello( "Place" );

Tastes Like Burning

Depending on what you're doing, you can crash the application just like you're accustomed to doing in straight C/C++. It will feel just like home, I promise. Some of you will happily accept this for the gains in power, but it does raise the lethality of your Lua code quite handily.

Luckily, in many cases where it can determine that you've just done some naughty, sane errors will come out through Lua. So type mismatches and incorrect parameter counts won't bring everything to its knees.

Recommendations That Likely Over-step Their Bounds

I generally prefer to not use metatables and to author my Lua code in a more functional style. Additionally, I've found it invaluable to use integer handles to represent objects from known pools, especially for any scripting layer. It has the added benefit of further forcing the Lua code to be somewhat dumb in that it will require extra code and work to get to other pieces of data in order to manipulate them.

I've found this to make it easier to remain mindful of what data one is actually manipulating and make the code more portable for those long, cut-and-paste-hero sessions that we all love to pretend don't happen as often as they do.

Conclusion

Go have a look at the LuaJIT site to get a full explanation of this extension and some of the finer points of using it that may be non-obvious from the specification.

-r