FFI C declaration composer
This example illustrates how to generate foreign function interface
(FFI) bindings for a C library using the Lua plugin for GCC and the
ffi-cdecl
module. The C API of the library is extracted
using a C source file that contains capture macros, and matching FFI C
bindings are generated using the C declaration composer for GCC.
After following the installation instructions, you may run the example:
make -C ffi-cdecl
If the GCC plugin is not installed in the GCC plugin directory:
make -C ffi-cdecl GCCLUA=$HOME/projects/gcc-lua/gcc/gcclua.so
The FFI C bindings are written to the file C.lua
:
local ffi = require("ffi")
.cdef[[
ffiint getopt(int, char *const *, const char *);
extern char *optarg;
typedef int clockid_t;
struct timespec {
long int tv_sec;
long int tv_nsec;
};
static const int RLIM_INFINITY = -1;
]]
1 Usage
To generate FFI C bindings for a library, we compose a Lua script for the GCC C compiler:
local gcc = require("gcc")
local cdecl = require("gcc.cdecl")
local fficdecl = require("ffi-cdecl")
-- Output generated assembly to /dev/null
.set_asm_file_name(gcc.HOST_BIT_BUCKET) gcc
First, we define a function that captures C declarations in a table:
-- Captured C declarations.
local decls = {}
-- Type declaration identifiers.
local types = {}
-- Parse C declaration from capture macro.
.register_callback(gcc.PLUGIN_PRE_GENERICIZE, function(node)
gcclocal decl, id = fficdecl.parse(node)
if decl then
if decl:class() == "type" or decl:code() == "type_decl" then
[decl] = id
typesend
table.insert(decls, {decl = decl, id = id})
end
end)
Second, we output FFI C bindings for the captured declarations to a Lua file:
-- Formats the given declaration as a string of C code.
local function format(decl, id)
if decl:class() == "constant" then
return "static const int " .. id .. " = " .. decl:value()
end
return cdecl.declare(decl, function(node)
if node == decl then return id end
return types[node]
end)
end
-- Output captured C declarations to Lua file.
.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
gcclocal result = {}
for i, decl in ipairs(decls) do
[i] = format(decl.decl, decl.id) .. ";\n"
resultend
local f = assert(io.open(arg.output, "w"))
:write([=[
flocal ffi = require("ffi")
ffi.cdef[[
]=], table.concat(result), [=[
]]
-- Load POSIX real time extensions into global namespace.
local C = ffi.C
if not pcall(function() return ffi.C.clock_gettime end) then
C = ffi.load("rt", true)
end
return C
]=])
:close()
fend)
The script is executed by loading the Lua plugin for GCC as follows:
gcc -S C.c -fplugin=gcclua -fplugin-arg-gcclua-script=C.lua.in -fplugin-arg-gcclua-output=C.lua
If the GCC plugin is not installed in the GCC plugin directory:
gcc -S C.c -fplugin=$HOME/projects/gcc-lua/gcc/gcclua.so -fplugin-arg-gcclua-script=C.lua.in -fplugin-arg-gcclua-output=C.lua
2 C capture files
For the purpose of capturing the C API of a library, the C header
ffi-cdecl.h
defines a range of capture macros. These macros are used in a
C source file that includes the header(s) of the library. Each capture
macro receives an identifier that refers to a C declaration or
preprocessor constant.
Consider the following capture file C.c
for the POSIX C
API:
#define _XOPEN_SOURCE 700
#include <libgen.h>
#include <sys/resource.h>
#include <time.h>
#include <unistd.h>
#include "ffi-cdecl.h"
/* capture a function */
(getopt)
cdecl_func/* capture a global variable */
(optarg)
cdecl_var/* capture a type declaration */
(clockid_t)
cdecl_type/* capture a struct */
(timespec)
cdecl_struct/* capture a constant */
(RLIM_INFINITY) cdecl_const
The identifier in the generated FFI C binding may be overridden using the preprocessor:
#define mygetopt getopt
(mygetopt) cdecl_func
3 C capture macros
The C header ffi-cdecl.h
defines the following capture
macros:
cdecl_type(id)
-
Captures the type declaration with the given identifier.
cdecl_memb(id)
-
Captures the type definition of a struct, union or enum type with the given identifier.
cdecl_struct(tag)
-
Captures the type definition of a struct type with the given tag.
cdecl_union(tag)
-
Captures the type definition of a union type with the given tag.
cdecl_enum(tag)
-
Captures the type definition of an enumeration type with the given tag.
cdecl_func(id)
-
Captures the function declaration with the given identifier.
Any subexpression containing an address-of, cast, or comma operator is replaced by its right-most operand.
cdecl_var(id)
-
Captures the variable declaration with the given identifier.
Any subexpression containing an address-of, cast, or comma operator is replaced by its right-most operand.
cdecl_const(id)
-
Captures the integer constant with the given identifier.
Any subexpression containing an address-of, cast, or comma operator is replaced by its right-most operand.