公開技術情報

[English] [Japanese]

22.1. glue コードの半自動生成

Lua の外部モジュールには、次の 2 つのケースがある。

  • Lua スクリプトで構成されるケース
  • C 等の Native Library を利用するケース

Lua から Native Library を利用するには、 Lua 用のインタフェースを橋渡しする glue が必要である。

LuneScript は、この glue を半自動的に生成する機能を持つ。

LuneScript の glue 自動生成機能は、 swig に代表される C/C++ 等のソースから Lua の glue を自動生成する機能ではなく、 LuneScript で module 宣言したモジュールの glue コードを自動生成する機能である。

swig:		C/C++ ==> glue
LuneScript:	Lns   ==> glue

使用方法

glue を生成するには、 module 定義時に glue キーワードを指定する。

次は、 test.ext モジュールの glue を生成するサンプルである。

// @lnsFront: skip
module External require "test.ext" glue 'hoge_' {
   pub static fn create( val:int ): External;
   pub fn add( val:int ):int;
}

let obj = External.create( 1 );
print( obj.add( 10 ) ); // 11

ここで指定した "test.ext" と 'hoge_' は、 glue を生成する際に利用される。

このソースから glue コードを生成するには、次のコマンドを実行する。

$ lua lune/base/base.lua src.lns glue [dir]

ここで、 src.lns は入力元のソースで、 dir は出力先のディレクトリのパスを指定する。

このコマンドによって、次の 2 つの glue コードが生成される。

  • test_ext_glue.c
  • test_ext_glue.h

test_ext_glue.c は、次の内容を持つ。

#include "test_ext_glue.h"
static int lns_glue_create( lua_State * pLua );
static int lns_glue_add( lua_State * pLua );
static const char * s_full_class_name = "test_ext";
static const luaL_Reg s_lua_func_info[] = {
  { "create", lns_glue_create },
  { NULL, NULL }
};
static const luaL_Reg s_lua_method_info[] = {
  { "add", lns_glue_add },
  { NULL, NULL }
};
void * lns_glue_get_test_ext( lua_State * pLua, int index )
{
    return luaL_checkudata( pLua, index, s_full_class_name);
}

static void lns_glue_setupObjMethod(
    lua_State * pLua, const char * pName, const luaL_Reg * pReg )
{
    luaL_newmetatable(pLua, pName );
    lua_pushvalue(pLua, -1);
    lua_setfield(pLua, -2, "__index");

#if LUA_VERSION_NUM >= 502
    luaL_setfuncs(pLua, pReg, 0);

    lua_pop(pLua, 1);
#else
    luaL_register(pLua, NULL, pReg );

    lua_pop(pLua, 1);
#endif
}

void * lns_glue_new_test_ext( lua_State * pLua, size_t size )
{
    void * pBuf = lua_newuserdata( pLua, size );
    if ( pBuf == NULL ) {
        return NULL;
    }
    
#if LUA_VERSION_NUM >= 502
    luaL_setmetatable( pLua, s_full_class_name );
#else
    luaL_getmetatable( pLua, s_full_class_name );
    lua_setmetatable( pLua, -2 );
#endif

    return pBuf;
}

int luaopen_test_ext( lua_State * pLua )
{
    lns_glue_setupObjMethod( pLua, s_full_class_name, s_lua_method_info );

#if LUA_VERSION_NUM >= 502
    luaL_newlib( pLua, s_lua_func_info );
#else
    luaL_register( pLua, s_full_class_name, s_lua_func_info );
#endif
    return 1;
}
static int lns_glue_add( lua_State * pLua ){
  int val = 0;
  val = luaL_checkinteger( pLua, 2 );
  return hoge_add( pLua, val);
}
static int lns_glue_create( lua_State * pLua ){
  int val = 0;
  val = luaL_checkinteger( pLua, 1 );
  return hoge_create( pLua, val);
}

この test_ext_glue.c は、 LuneScript で定義している create()add() のメソッドを Lua に登録するコードである。

LuneScript で定義している create()add() に対応する関数は、 lns_glue_create()lns_glue_add() である。

なお、この関数はそれぞれ hoge_create()hoge_add() をコールしている。 この hoge_ は、 LuneScript のコード上で指定した glue の文字列が利用される。

test_ext_glue.h は次の内容を持つ。

#include <lauxlib.h>
extern int hoge_create( lua_State * pLua, int val );
extern int hoge_add( lua_State * pLua, int val );
extern int luaopen_test_ext( lua_State * pLua );
extern void * lns_glue_get_test_ext( lua_State * pLua, int index );
extern void * lns_glue_new_test_ext( lua_State * pLua, size_t size );

ここで、=hoge_create()= と hoge_add() は次の型となっている。

extern int hoge_create( lua_State * pLua, int val );
extern int hoge_add( lua_State * pLua, int val );

第一引数の lua_State * と戻り値の int は、 Lua の glue インタフェースと同じ意味を持つ。

第二引数の int は、 LuneScript で定義しているメソッドの引数が入る。 これは、 LuneScript で自動生成している test_ext_glue.c が Lua のスタックから値を取り出して値を設定している。

Lua の glue では、関数引数を得るため API をコールする必要があるが、 LuneScript で glue を生成した場合は、 glue コード内で関数引数を得る API を実行している。 これにより、 ユーザは関数引数を得る API をコールする必要はない。

ただし、LuneScript 側で定義しているメソッドの引数の型が int(int!), real(real!), str(str!) 型以外だった場合、 LuneScript で生成する glue では対応できないため、 別途ユーザ側での対応が必要である。

また glue の戻り値については、 ユーザ側で Lua の glue インタフェースと同じ制御を行なう必要がある。

hoge_create()hoge_add() を別途定義することで、 test.ext モジュールが完成する。

例えば次のように定義する(glue.c)。

// glue.c
#include <test_ext_glue.h>

typedef struct {
    int val;
} glue_t;

int hoge_create( lua_State * pLua, int val )
{
    glue_t * pGlue = (glue_t*)lns_glue_new_test_ext( pLua, sizeof( glue_t ) );
    pGlue->val = val;
    return 1;
}

int hoge_add( lua_State * pLua, int val )
{
    glue_t * pGlue = lns_glue_get_test_ext( pLua, 1 );
    lua_pushinteger( pLua, val + pGlue->val );
    return 1;
}

ちなみにビルドは次のように行なう。

$ gcc -std=c99 -fPIC -shared -o test/ext.so glue.c test_ext_glue.c