LuaJITのFFI拡張機能実践ガイド

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

タグ: LuaJIT FFI パラメータ化型 Cバインディング コールバック処理

6月1日 09:26 投稿