LuaJITのC言語との型変換実験

LuaJITのFFIライブラリが,FFI(Foreign Function Interface)を通した時にインターフェース越しに引数がどのように相互変換されるかは,一応ドキュメントになっています.
FFI Semantics

でも良くわからないことが多いので,いろいろとサンプルを作ってみました.


Part 1: Hello, World with Standard C Library

Lua版は,普通に「Hello, World」を表示します.
FFIを通したバージョンは,Cの標準関数のprintfを通して「Hello, World」を表示します.
プログラムは,1つのLuaスクリプトにまとめました.

-- Pure Lua
local msg = "Hello, World"
print(msg)

-- FFI
local ffi = require("ffi")
ffi.cdef[[
  int printf(const char *fmt, ...);
]]
ffi.C.printf("%s\n", msg)
> luajit extend_LuaJIT.lua
Hello, World
Hello, World

同じ結果が得られるはずです.

Part 2: Hello, World with Original C Library

「Original」は,元々の,じゃなくて独自のの意味で使いました.
普通にLuaで「Hello, World」を表示するものと,Cでprintfを呼び出すラッパーのprint関数を書いて,その独自のprint経由で「Hello, World」を表示するプログラムを書きました.
Cのプログラム及びそれを共有ライブラリ化する手順が追加されます.

こちらがC言語のプログラム.

#include <stdio.h>

void print(const char *s)
{
  printf("%s\n", s);
}

こちらは,LuaのHello, World表示プログラムと,C言語のラッパーを通してHello, Worldするプログラムを書き並べたLuaスクリプト

-- Pure Lua
local msg = "Hello, World"
print(msg)

-- FFI
local ffi = require("ffi")
local ex = ffi.load("extend_lua")
ffi.cdef[[
  void print(const char *s);
]]
ex.print(msg)

こちらは,共有ライブラリ作成の一例と,それを呼び出すLuaスクリプトを実行してみた例.

> cc -fPIC -shared extend_lua.c -o libextend_lua.so
> env LD_LIBRARY_PATH=. luajit extend_LuaJIT.lua
Hello, World
Hello, World

同じ結果が得られるはずです.

Part 3: Hello, World using Pointer to Pointers

C言語ポインターへのポインターに挑戦してみました.
C言語のラッパーでは,文字列を格納するポインタへのポインタ領域を2つ確保し,それぞれに"Hello"と"World"をセットし,2つの文字列を並べて表示しました.

こちらは,2つのポインタ領域を獲得する関数と,文字列のポインタを獲得した領域にセットする関数,及びそれらの2つのポインタが表す文字列を表示するラッパー.

#include <stdio.h>
#include <stdlib.h>
const char ** make_pp(int n)
{
  const char **s = (const char **)malloc(sizeof(char *)*n);
  return(s);
}
void set(char **s, char *ss, int i)
{
  s[i] = ss;
}
void print(const char **s)
{
  printf("%s, %s\n", s[0], s[1]);
}

こちらの一連のLuaスクリプトは,普通にLuaで「Hello, World」を表示する関数と,領域を確保し,その後「Hello」と「World」領域にセットし,最期に表示するFFI利用の例.

-- Pure Lua
local msg = "Hello, World"
print(msg)

-- FFI
local ffi = require("ffi")
local ex = ffi.load("extend_lua")
ffi.cdef[[
  const char ** make_pp(int n);
  void set(const char **s, const char *ss, int i);
  void print(const char **s);
]]
local msg_p = ex.make_pp(2)
ex.set(msg_p, ffi.cast("const char *", "Hello"), 0)
ex.set(msg_p, ffi.cast("const char *", "World"), 1)
ex.print(msg_p)

実行するにあたって,念の為に共有ライブラリのパスを指定してみた例.

> env LD_LIBRARY_PATH=. luajit extend_LuaJIT.lua
Hello, World
Hello, World

同じ結果が得られるはずです.

Part 4: Hello World using Structure

#include <stdio.h>
#include <stdlib.h>

typedef struct { size_t n; const char **s; } array_pp;

array_pp *make_pp(int n)
{
  array_pp *x = (array_pp *)malloc(sizeof(array_pp));
  x->s = (const char **)malloc(sizeof(const char *) * n);
  x->n = n;
  return(x);
}
void set(array_pp *x, const char *s, int i)
{
  if (i < x->n)
  {
    x->s[i] = s;
  }
}
void print(array_pp *x)
{
  int i;
  for (i=0; i<x->n; ++i)
  {
    printf("%s", x->s[i]);
    if(i< x->n -1) printf(", ");
  }
  printf("\n");
}
-- Pure Lua
local msg = "Hello, World"
print(msg)

-- FFI
local ffi = require("ffi")
local ex = ffi.load("extend_lua")
ffi.cdef[[
  typedef struct { size_t n; const char **s; } array_pp;
  array_pp *make_pp(int n);
  void set(array_pp *x, const char *s, int i);
  void print(array_pp *x);

]]
local msg_p = ex.make_pp(2)
ex.set(msg_p, ffi.cast("const char *", "Hello"), 0)
ex.set(msg_p, ffi.cast("const char *", "World"), 1)
ex.print(msg_p)
> cc -Wall -fPIC -shared extend_lua.c -o libextend_lua.so
> env LD_LIBRARY_PATH=. luajit extend_LuaJIT.lua
Hello, World
Hello, World
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef char * msg;
typedef struct { size_t n; msg *s; } msgs;
typedef struct { size_t n; msgs **s; } msgs_list;

msgs_list *make_pp(int n)
{
  msgs_list *x = (msgs_list *)malloc(sizeof(msgs_list));
  x->s = (msgs **)malloc(sizeof(msgs *) * n);
  x->n = n;
  return(x);
}
msgs * make_msg(const char *s)
{
  msgs *x = (msgs *)malloc(sizeof(msgs));
  x->s = (msg *)malloc(strlen(s));
  strncpy((void *)x->s, (void *)s, strlen(s));
  x->n = strlen(s);
  return(x);
}

void set(msgs_list *x, msgs *s, int i)
{
  if (i < x->n)
  {
    x->s[i] = s;
  }
}
void print(msgs_list *x)
{
  int i;
  for (i=0; i<x->n; ++i)
  {
    printf("%s", (char *)x->s[i]);
    if(i< x->n -1) printf(", ");
  }
  printf("\n");
}
-- Pure Lua
local msg = "Hello, World"
print(msg)

-- FFI
local ffi = require("ffi")
local ex = ffi.load("extend_lua")
ffi.cdef[[
 typedef char * msg;
  typedef struct { size_t n; msg *s; } msgs;
  typedef struct { size_t n; msgs **s; } msgs_list;

  msgs_list *make_pp(int n);
  msgs * make_msg(const char *s);
  void set(msgs_list *x, msgs *s, int i);
  void print(msgs_list *x);

]]
local msgs = {"Hello", "World!"}
local msg_pp = ex.make_pp(#msgs)
for i,v in ipairs(msgs) do
  ex.set(msg_pp, ffi.cast("msgs *", v), i-1)     
end
ex.print(msg_pp)          
> env LD_LIBRARY_PATH=. luajit extend_LuaJIT.lua
Hello, World
Hello, World