tech

[English] [Japanese]

22.1. Semi-automatic generation of glue code

Lua's external modules have two cases:

  • A case composed of Lua scripts
  • When using Native Library such as C

In order to use Native Library from Lua, glue is needed to bridge the interface for Lua.

LuneScript has a function to generate this glue semi-automatically.

LuneScript's automatic glue generation function is not a function that automatically generates Lua glue from C/C++ sources such as swig, but a function that automatically generates glue code for modules declared in LuneScript.

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

how to use

To generate glue, specify the glue keyword when defining the module.

Here is a sample to generate glue for the test.ext module.

// @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" and 'hoge_' specified here are used when generating glue.

To generate glue code from this source, run the following command:

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

where src.lns is the source of the input and dir specifies the path of the destination directory.

This command generates two glue codes:

  • test_ext_glue.c
  • test_ext_glue.h

test_ext_glue.c has the following contents.

#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);
}

This test_ext_glue.c is the code to register create() and add() methods defined in LuneScript to Lua.

The functions corresponding to create() and add() defined in LuneScript are lns_glue_create() and lns_glue_add().

Note that these functions call hoge_create() and hoge_add() respectively. This hoge_ uses the glue string specified in the LuneScript code.

test_ext_glue.h has the following contents.

#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 );

where hoge_create() and hoge_add() are of the following types:

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

The first argument lua_State * and the return value int have the same meaning as Lua's glue interface.

The second argument int contains the argument of the method defined in LuneScript. This is because test_ext_glue.c, which is automatically generated by LuneScript, extracts the value from the Lua stack and sets the value.

Lua's glue needs to call an API to get the function arguments, but if you generate the glue with LuneScript, you're doing the API to get the function arguments inside the glue code. This way the user doesn't have to call the API to get the function arguments.

However, if the argument type of the method defined on the LuneScript side is other than int(int!), real(real!), str(str!), glue generated by LuneScript cannot handle it. It is necessary to take measures on the side.

Also, the return value of glue needs to be controlled by the user side in the same way as Lua's glue interface.

The test.ext module is completed by defining hoge_create() and hoge_add() separately.

For example, define as follows (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;
}

By the way, build like this:

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