This is Alien version 0.4.1.
Alien is a Foreign Function Interface (FFI) for Lua. An FFI lets you call functions in dynamic libraries (.so, .dylib, .dll, etc.) from Lua code without having to write, compile and link a C binding from the library to Lua. In other words, it lets you write extensions that call out to native code using just Lua.
Be careful when you use Alien, I tried to make it as safe as possible, but it is still very easy to crash Lua if you make a mistake. Alien itself is not as robust as a standard Lua extension, but you can use it to write extensions that won't crash if you code them well.
Alien works on Unix-based systems and Windows. It has been tested on Linux x86, Linux x64, Linux ARM, FreeBSD x86, Windows x86, OSX x86, and OSX PPC. The Windows binary uses MSVCR80.DLL for compatibility with LuaBinaries.
The best way to install Alien is through LuaRocks. Just add this repository to your LuaRocks configuration, then do luarocks install alien
. You may need root permissions to do this, depending on your LuaRocks configuration.
Go to the Alien rock directory to see local copies of this documentation, as well as the test suite. If you are in the path of the test suite (tests
) you can run the suite with:
lua -l luarocks.require test_alien.lua
If everything is ok you should see no output.
Alien installs to modules, alien
and alien.struct
. The latter is a slightly modified version of Roberto Ierusalimschy's struct library that can unpack binary blobs (userdata) instead of just strings.
Loading a dynamic library is very simple; Alien by default assumes a naming scheme of libname.dylib for OSX and libname.so for other Unix systems. If name is not one of the functions the alien
module exports then you can get a reference to the library with alien.
name
. Otherwise (for example, to load a library called libwrap.so) you have to use alien.load("wrap")
.
You can also specify the full name of the library by calling alien.load
with a path or with the appropriate extension, such as alien.load("mylibs/libfoo.so")
or alien.load("libfoo.so")
. Either way you get back a reference to the library that you will use to access its functions.
You can also get a reference to the currently running module using alien.default
, this lets you get references to any function exported by the module and its transitive dependencies on ELF and Mach-O systems.
Once you have a reference to a library you can get a reference to an exported function with libref.funcname. For example:
> def=alien.default
> =def.puts
alien function puts, library defaults
>
To use a function you first have to tell Alien the function prototype, using func:types(ret_type, arg_types...), where the types are one of the following strings: "void", "int", "double", "char", "string", "pointer", "ref int", "ref double", "ref char", "callback", "short", "byte", "long", and "float". Most correspond directly to C types; byte is a signed char, string is const char*, pointer is void*, callback is a generic function pointer, and ref char, ref int and ref double are by reference versions of the C types. Continuing the previous example:
> def.puts:types("int", "string")
> def.puts("foo")
foo
>
As you can see, after defining the prototype you can call the function just as a Lua function. Alien converts Lua numbers to the C numeric types, converts nil to NULL and Lua strings to const char* for string, and converts nil to NULL and userdata to void* for pointer. The conversions work in reverse for the return value (with the pointer type converted to a light userdata).
By reference types are special; Alien allocates space on the stack for the argument, copies the Lua number you passed to it (converting appropriately), then calling the function with the address of this space. Then the value is converted back to a Lua number and returned after the function normal return value. An example, using scanf:
> scanf = alien.default.scanf
> scanf:types("int", "string", "ref int", "ref double")
> _, x, y = scanf("%i %lf", 0, 0)
23 42.5
> =x
23
> =y
42.5
You have to pass a value even if the function does not use it, as you can see above.
Another way to specify types is by passing a table to func:types. The array part of this table shoudl have one item for each parameter, and you can also pass two hash keys, ret, the function's return type (defaults to int
as usual), and abi, the function's calling convention (useful for Windows, where you can specify "stdcall" as the ABI for __stdcall
functions. The default ABI is always "default", and all systems also support "cdecl", the usual C calling convention. On systems that don't have the stdcall convention "stdcall" is the same as "default".
This is the previous example using this alternate definition:
> scanf = alien.default.scanf
> scanf:types{ ret = "int", "string", "ref int", "ref double" }
> _, x, y = scanf("%i %lf", 0, 0)
23 42.5
> =x
23
> =y
42.5
If you get raw function pointer (returned from a function, for example, or passed to a callback), you can turn it into an Alien function with alien.funcptr(fptr)
. This returns an Alien function object that you can type and call function normally.
The basic usage is enough to do a lot of interfacing with C code, specially with well-behaved libraries that handle their own memory allocation (the Lua C API is a good example of such an API). But there are libraries that do not export such a well-behaved API, and require you to allocate memory that is mutated by the library. This prevents you from passing Lua strings to them, as Lua strings have to be immutable, so Alien provides a buffer abstraction. The function alien.buffer
allocates a new buffer. If you call it with no arguments it will allocate a buffer with the standard buffer size for your platform. If call it with a number it will allocate a buffer with this number of bytes. If you pass it a string it will allocate a buffer that is a copy of the string. If you pass a light userdata it will use this userdata as the buffer (be careful with that).
After making a buffer you can pass it in place of any argument of string or pointer type. To get back the contents of the buffer you use buf:tostring
, and again you can either pass the number of characters to read from the buffer or don't pass anything, which treat the buffer as a C string (read until finding a \0). You can also call buf:len
, which calls strlen on the buffer. Finally, tostring(buf)
is the same as buf:tostring()
.
An example of how to use a buffer:
> gets = alien.default.gets
> gets:types("pointer", "string")
> buf = alien.buffer()
> gets(buf)
Foo bar
> =tostring(buf)
Foo bar
>
You can access the i-th character of a buffer with buf[i]
, and you can set its value with buf[i] = v
. Notice that these are C characters (bytes), not Lua 1-character strings, so you need to use string.char
and string.byte
to convert between Lua characters and C characters. Access to Alien buffers from Lua is 1-based instead of 0-based.
You can also get and set other values by using buf:get(offset, type), and set it by buf:set(offset, val, type). The offset is in bytes, not in elements, so if buf has three "int" values their offsets are 1, 5 and 9, respectively, assuming each "int" is four bytes long.
All get and set operations do no bounds-checking, so be extra careful, or use the safer alien.array abstraction that is built on top of buffers.
Arrays are buffers with an extra layer of safety and sugar on top. You create an array with alien.array(type, length)
, where type is the Alien type of the array's elements, and length is how many elements the array has. After creating an array arr you can get the type of its elements with arr.type, how many elements it has with arr.length, and the size (in bytes) of each element with arr.size. The underlying buffer is arr.buffer.
You can access the i-th element with arr[i], and set it with arr[i] = val. Type conversions are the same as with buffers, or function calls. Storing a string or userdata in an array pins it so it won't be collected while it is in the array.
For convenience alien.array
also accepts two other forms: alien.array(type, tab)
creates an array with the same length as tab and initializes it with its values; alien.array(type, length, buf)
creates an array with buf as the underlying buffer. You can also iterate over the array's contents with arr:ipairs()
.
The following example shows an use of arrays:
local function sort(a, b)
return a - b
end
local compare = alien.callback(sort, "int", "ref int", "ref int")
local qsort = alien.default.qsort
qsort:types("void", "pointer", "int", "int", "callback")
local nums = alien.array(t, { 4, 5, 3, 2, 6, 1 })
qsort(nums.buffer, nums.length, nums.size, compare)
for i, v in nums:ipairs() do print(v) end
This prints numbers one to six on the console.
Alien also provides three convenience functions that let you dereference a pointer and convert the value to a Lua type:
alien.tostring
takes a userdata (usually returned from a function that has a pointer return value), casts it to char*, and returns a Lua string. You can supply an optional size argument (if you don't Alien calls strlen on the buffer first).alien.toint
takes a userdata, casts it to int*, dereferences it and returns it as a number. If you pass it a number it assumes the userdata is an array with this number of elements.alien.toshort
, alien.tolong
, alien.tofloat
, and alien.todouble
are like alien.toint
, but works with with the respective typecasts.The numeric alien.totype functions take an optional second argument that tells how many items to unpack from the userdata. For example, if ptr is a pointer to an array of four floats, the following code unpacks this array:
> fs = alien.tofloat(ptr, 4)
> =#fs
4
>
Use these functions with extra care, they don't make any safety checks. For more advanced unmarshaling use the alien.struct.unpack
function.
A common pattern when wrapping objects from C libraries is to put a pointer to this object inside a full userdata, then associate this userdata with a metatable that is associated with a string tag. This tag is used to check if the userdata is a valid userdata in each function that uses it. As the userdata is a full userdata it also can have a __gc
metamethod for resource reclamation.
Alien has three functions that let you replicate this pattern on your extensions:
alien.tag(*tagname*)
creates a new metatable with tag tagname if one does not exist, or returns the metatable with this tag. The namespace for tags is global, so a good pattern is to prefix the tag name with the name of your module (such as mymod_mytag).alien.wrap(*tagname*, ...)
creates a full userdata, tags it with the metatable associated with tagname, stores the values you passed, then returns the full userdata. Valid values are nil, integers and other userdata.alien.unwrap(*tagname*, obj)
tests if obj is tagged with tagname, throwing an error if it is not, then returns the values previously stored in it.alien.rewrap(*tagname*, obj, ...)
replaces the elements on obj with new values. If you pass more values than obj had previously the extra values are silently ignored. If you pass less tehn obj is filled with nil.For example, suppose libfoo has a create_foo
function that returns a Foo*
object. These objects have to be properly disposed by calling destroy_foo
when they are not used anymore. This is easy to implement:
local tag_foo = alien.tag("libfoo_foo")
alien.foo.create_foo:types("pointer")
alien.foo.destroy_foo_types("void", "pointer")
function new_foo()
local foo = alien.foo.create_foo()
return alien.wrap("libfoo_foo", foo)
end
tag_foo = {
__gc = function (obj)
local foo = alien.unwrap("libfoo_foo", obj)
alien.foo.destroy_foo(foo)
end
}
Then on any function that operates on Foo*
types you first unwrap to get the pointer, then pass it to the function in libfoo.
Several operating system functions return errors on a special variable called errno. To get the value of errno with Alien call alien.errno()
.
Some libraries have functions that take callbacks, functions that get called by the library. Most GUI libraries use callbacks, but even the C library has qsort. Alien lets you create a callback from a Lua function with alien.callback
. You pass the function and the callback prototype that the library expects. Alien will return a callback object that you can pass in any argument of callback type. A simple example, using qsort:
local function cmp(a, b)
return a - b
end
local cmp_cb = alien.callback(sort, "int", "ref char", "ref char")
local qsort = alien.default.qsort
qsort:types("void", "pointer", "int", "int", "callback")
local chars = alien.buffer("spam, spam, and spam")
qsort(chars, chars:len(), alien.sizeof("char"), cmp_cb)
assert(chars:tostring() == " ,,aaaadmmmnpppsss")
The qsort function sorts an array in-place, so we have to use a buffer.
Callbacks are callable from Lua just like any other Alien function, although you can't change their types.
C libraries are full of symbolic constants that are in truth magic numbers, as they are replaced by the preprocessor before even the C compiler has a chance to see them. This means that all these constants are on header files. This also includes things such as the layout and size of strucutres the library depends on. All this information can change from version to version of the library, or from platform to platform.
Alien provides a utility script called constants that makes it easier to work with these numbers. This utility takes three arguments on the command line: a definitions file, the name of the C file you want it to generate, and the name of a Lua file that the C file will generate when compiled and run. The definitions file can contain preprocessor directives, blank lines, and lines with definitions either of the form identifier or lua_identifier = c_identifier. The first form is equivalent to identifier = identifier. It is best to explain by example (from a libevent binding):
#include <sys/types.h>
#include <event.h>
EV_SIZE = sizeof(struct event)
EV_READ
EV_WRITE
EV_TIMEOUT
EVLOOP_NONBLOCK
EVLOOP_ONCE
Lines with preprocessor directives are copied verbatim to the C file constants generates. The above definitions file generates this C file:
/* Generated by Alien constants */
#include <stdio.h>
#include <sys/types.h>
#include <event.h>
#define LUA_FILE "event_constants.lua"
int main() {
FILE *f = fopen(LUA_FILE, "w+");
fprintf(f, "-- Generated by Alien constants\n\n");
fprintf(f, "%s = %i\n", "EV_SIZE ", sizeof(struct event));
fprintf(f, "%s = %i\n", "EV_READ", EV_READ);
fprintf(f, "%s = %i\n", "EV_WRITE", EV_WRITE);
fprintf(f, "%s = %i\n", "EV_TIMEOUT", EV_TIMEOUT);
fprintf(f, "%s = %i\n", "EVLOOP_NONBLOCK", EVLOOP_NONBLOCK);
fprintf(f, "%s = %i\n", "EVLOOP_ONCE", EVLOOP_ONCE);
fclose(f);
}
Which, when compile and run, generates this file on a Linux/Intel system:
-- Generated by Alien constants
EV_SIZE = 84
EV_READ = 2
EV_WRITE = 4
EV_TIMEOUT = 1
EVLOOP_NONBLOCK = 2
EVLOOP_ONCE = 1
These steps (generating the C file, compiling, generating the Lua file) are best done in the build step of your extension.
You can query what platform your extension is running on with alien.platform
. Right now this can be either "unix" or "osx". Other platforms will be added as they are tested. You can use this information for conditional execution in your extensions.
You can get the sizes of the types Alien supports using alien.sizeof(*typename*)
, as the qsort example in the Callbacks section shows. You can also get strucutre aligment information with alien.align(*typename*)
.
Several extensions may need to create Lua tables with preallocated array and/or hash parts, for performance reasons (implementing a circular queue, for example). Alien exposes the lua_createtable
function as alien.table(narray, nhash)
.
Alien is designed and implemented by Fabio Mascarenhas. It uses the great libffi library by Anthony Green (and others) to do the heavy lifting of calling to and from C. The name is stolen from Common Lisp FFIs.
Alien's uses the MIT license, reproduced below:
Copyright (c) 2008 Fabio Mascarenhas
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.