LuaJIT has an FFI which allows code to be JIT compiled, whereas regular C API calls aren't 1:
- Easier bindings done from the Lua side, given a simple C ABI of whatever needs binding. No unexpected code being generated that may present edge-case bugs which might be difficult to detect.
- Safety. Third-party scripting can easily crash the application
- Bindings seem to be simpler from a C-language perspective. To this end, C++ programs would presumably have to write C ABI functions to be picked up/wrapped on Lua's end via the FFI 2. This contrasts with automatically generated SWIG bindings from C++ to Lua's C API. However, I think a benefit is that it ends up being cleaner and more precise/predictable than generated code.
Using the FFI in LuaJIT is pretty straightforward. A shared library has to be created that exposes the C functions:
__declspec(dllexport) on Windows. This can then be loaded by a Lua script:
Official FFI Resources
C wrapper development resources:
There is a heavy overhead with C-to-Lua calls, generally found in the form of callbacks in which a C FFI function is provided a callback function written in Lua. The overhead is seemingly similar to the C API calls.
Do not use callbacks for performance-sensitive work: e.g. consider a numerical integration routine which takes a user-defined function to integrate over. It's a bad idea to call a user-defined Lua function from C code millions of times. The callback overhead will be absolutely detrimental for performance.
It's considerably faster to write the numerical integration routine itself in Lua — the JIT compiler will be able to inline the user-defined function and optimize it together with its calling context, with very competitive performance.
Instead of passing a callback to a C function that applies that function to a sequence, a C function should be created that generates/yields the sequence an element at a time, with the processing done in Lua:
For new designs avoid push-style APIs: a C function repeatedly calling a callback for each result. Instead use pull-style APIs: call a C function repeatedly to get a new result. Calls from Lua to C via the FFI are much faster than the other way round. Most well-designed libraries already use pull-style APIs (read/write, get/put).
Here is a benchmark of the forms of callbacks.
Resources about the language:
Resources on LuaJIT:
- Scripting with LuaJIT and selectively sandboxing the FFI
- FFI Embedding Talk #1
- FFI Embedding Talk #2
Projects using LuaJIT FFI:
In LuaJIT FFI/C++ binding, best approach?, someone decided to use clang to parse each method and generate C wrappers. Others do it manually.