Alien is a Lua binding to libffi.
Here is an example of binding to the C runtime; note that Windows is a special case requiring an explicit load. Alien handles the tedious part of binding to C for us:
require "alien"
if alien.platform == "windows" then
libc = alien.load("msvcrt.dll")
else
libc = alien.default end libc.malloc:types("pointer", "int")
libc.free:types("void", "pointer")
libc.strcpy:types("void", "pointer", "string")
libc.strcat:types("void", "pointer", "string")
libc.puts:types("void", "string")
local foo = libc.malloc(string.len("foo") + string.len("bar") + 1)
libc.strcpy(foo, "foo")
libc.strcat(foo, "bar")
libc.puts(foo)
libc.strcpy(foo, "bar")
libc.puts(foo)
libc.puts("Yeah!")
libc.free(foo)
Here is the classic GTK+ "Hello, World":
local gtk,p,i=alien.load("/usr/lib/libgtk-x11-2.0.so.0"),"pointer","int"
gtk.gtk_init:types(nil,p,p)
gtk.gtk_message_dialog_new:types(p,p,i,i,i,p)
gtk.gtk_dialog_run:types(i,p)
gtk.gtk_init(nil,nil)
gtk.gtk_dialog_run(gtk.gtk_message_dialog_new(nil,0,0,1,"Alien Rocks!"))
People can get a little bit overexcited at this point and to try doing non-trivial GTK+ applications with Alien, but there are a lot of functions and constants involved; you are better off with a full-featured GTK binding like lua-gtk. But if you just want your script to put up a message, it works fine.
One issue that may bite you with Alien is that you need the full path to the shared library on Unix (Windows has a more relaxed shared library path) and these paths will move around depending on the whim of the distribution maintainers. For example,
the shared libraries are in /usr/lib64
on RedHat/SuSE x86_64 distributions (but not in Debian/Ubuntu).
How fast is Alien? Calling sin
through Alien takes about 3 times longer than math.sin
. So there is a definite cost for accessing functions in this way, but in many cases the actual work done by the function is much larger than the call overhead.
LuaJIT's built-in FFI provides a very fast way for Lua programs to access C functions and structures. Provided LuaJIT supports your platform, it will be as fast (if not faster) than writing C extensions. Simple C function and structure declarations are directly understood:
ffi.cdef[[
void Sleep(int ms);
]]
after which the function can be called directly as ffi.C.sleep
.
C arrays and structures can be directly created and accessed - note that arrays are zero-based:
> ffi = require 'ffi'
> arr = ffi.new("double[?]",20)
> for i = 1,20 do arr[i-1] = i end
> = arr[10];
11
> ffi.cdef [[struct _point {double x,y;};]]
> point = ffi.new("struct _point")
> point.x, point.y = 10,20
> = point.x, point.y
10 20