LuaJITのFFI(Foreign Function Interface)は、LuaコードからC言語ライブラリを直接呼び出すための強力な仕組みです。この機能を活用することで、高パフォーマンスな外部ライブラリとシームレスに連携することが可能です。以下では、FFIの高度な利用法と実際のバインディング例を解説します。
主要なFFIバインディングカテゴリ
- システム基盤:ljsyscall(Linuxシステムコール)、luapower/winapi(Win32 API)、luapower/pthread(POSIXスレッド)
- データベース:lsqlite3-ffi(SQLite)、mongolua(MongoDB)
- グラフィックス:LuaJIT-SDL2、luapower/cairo(Cairoライブラリ)
- 音声/映像:ffmpeg-lua-ffi、openal-lua-ffi
- ネットワーク:lua-zmq(ZeroMQ)、luajit-ffi-enet
- 暗号化:luapower/sha2、lua-resty-scrypt
多くの開発者は、必要な機能のみを限定的にバインディングします。例えば、zlibのバインディングは数行で実現可能です。
パラメータ化型の活用
FFIのパラメータ化型は、C型宣言を動的に生成する機能です。LuaJIT 2.0.1以降でサポートされています。
local matrix = ffi.typeof("float[$][$]", rows, cols)
local array = ffi.typeof("$*", int_type)
パラメータの種類はctype、string、numberです。例えば:
- ctype/cdata:型の派生に使用。例:ffi.typeof("$*", ct) はctへのポインタ
- string:識別子やキーワードとして使用
- number:整数値として使用(配列サイズなど)
汎用スタックの実装例:
local function create_stack(elem_type)
local stack_type = ffi.typeof("struct { int size; int capacity; $* data; }", elem_type)
return ffi.metatype(stack_type, {
__new = function(tp, cap)
return ffi.new(tp, cap, 0, ffi.new(elem_type, cap))
end,
push = function(self, val)
if self.size >= self.capacity then error("overflow") end
self.data[self.size] = val
self.size += 1
end,
pop = function(self)
if self.size <= 0 then error("underflow") end
self.size -= 1
return self.data[self.size]
end
})
end
local int_stack = create_stack(ffi.typeof("int"))
int_stack:push(42)
print(int_stack:pop()) -- 42
コールバック制限の回避策
C APIのコールバックで値渡しの構造体を使用する場合、FFIでは直接扱えません。ポインタ経由でアクセスする方法があります。
local function create_cursor_visitor(storage)
return function(cursor_ptr, parent_ptr, client_data)
local cursor = ffi.new("CXCursor", cursor_ptr[0])
table.insert(storage, cursor)
return lib.CXChildVisit_Continue
end
end
local storage = {}
local callback = ffi.cast("CXCursorVisitor_WithPointers", create_cursor_visitor(storage))
lib.clang_visitChildren(cptr, callback, nil)
FFI技術知識
cdataポインタをLua数値に変換
local address = tonumber(ffi.cast("uintptr_t", ptr))
構造体内の定数
ffi.cdef[[
struct example {
static const int VALUE = 100;
};
]]
local obj = ffi.new("struct example")
print(obj.VALUE) -- 100