C と lua の連携方法メモ

  • 27
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

初期化

init.cpp
lua_State* l = luaL_newstate(); // lua context 取得
luaL_openlibs(l);               // 基本的ライブラリ読み込み

C から lua script の読み込みと実行

init.cpp
lua_State* l = luaL_newstate(); // lua context 取得
luaL_openlibs(l);               // 基本的ライブラリ読み込み

// 文字列を lua script として実行
luaL_dostring(l, "print('hello lua script!!')");

// lua script file 読み込み
if (luaL_loadfile(l, fname)) {
  fprintf(stderr, "cannot open %s\n", fname);
  return;
}
if (lua_pcall(l
              , 0     // 引き数の数
              , 0     // 戻り値の数
              , 0)) { // error message handler の stack index
  fprintf(stderr, "cannot exec %s\n", fname);
  return;
}

C から lua の関数呼び出し

下記の lua 関数を C から呼び出す.

func.lua
function func(arg1, arg2)
  print(arg1)
  print(arg2)
  return arg1+1, arg2+2
end

上記を呼び出す C のコード.

call.cpp
lua_State* l = luaL_newstate();
luaL_openlibs(l);
luaL_loadfile(l, "func.lua");
lua_pcall(l, 0, 0, 0); // script を実行しておかないと関数を呼び出せない
lua_getglobal(l, "func");   // 呼び出す関数
lua_pushnumber(l, 4);   // 第一引数
lua_pushnumber(l, 5);   // 第ニ引数
if (lua_pcall(l, 2, 2, 0)) {    // 引数 2 個, 戻り値 2 個
  fprintf(stderr, "cannot exec add. %s\n", lua_tostring(l, -1));
  return;
}
if (lua_isnumber(l, -1)) {
  int ret = lua_tointeger(l, -1); // 戻り値その 1
  printf("ret=%d\n", ret);
}
lua_pop(l, 1);      // 戻り値を pop
if (lua_isnumber(l, -1)) {
  int ret = lua_tointeger(l, -1); // 戻り値その 2
  printf("ret=%d\n", ret);
}
lua_pop(l, 1);      // 戻り値を pop

lua から C の関数呼び出し

下記のように lua から C の関数 c_func() を呼び出す.

func.lua
arg1, arg2 = 3, 4
ret1, ret2 = c_func(arg1, arg2)

上記から呼び出される C のコード.

call.cpp
// lua から呼ばれる関数
static int
c_func(lua_State* l)
{
  int arg1 = luaL_checkint(l, -2);  // 第一引数取得
  int arg2 = luaL_checkint(l, -1);  // 第二引数取得
  // pop は lua library がやってくれるので不要
  int ret1 = arg1 + 1;
  int ret2 = arg2 + 2;
  lua_pushnumber(l, ret1);  // 戻り値 1 を push
  lua_pushnumber(l, ret2);  // 戻り値 2 を push
  return 2;         // 戻り値の数を返す
}

// lua script を実行するコード
int main(int ac, char* av[])
{
  lua_State* l = luaL_newstate();
  luaL_openlibs(l);
  luaL_loadfile(l, "func.lua");
  lua_register(l, "c_func", c_func); // lua に関数を登録
  lua_pcall(l, 0, 0, 0); // script 実行
  lua_close(l);
}

C から lua へ table を渡す

C から下記 lua 関数 pass_table() を呼び出す.
また、この関数から table を戻り値として C に返す.

func.lua
function pass_table(t)
  for key, val in pairs(t) do
    print(key, val)
  end
  return t
end

lua script に table を引き数として渡し, 戻り値として table を受け取る.

call.cpp
lua_State* l = luaL_newstate();
luaL_openlibs(l);
luaL_loadfile(l, "func.lua");
lua_pcall(l, 0, 0, 0); // script を実行しておかないと関数を呼び出せない
lua_getglobal(l, "pass_table"); // 呼び出す関数
lua_newtable(l); // table を新規作成し, stack に積む
lua_pushstring(l, "key1");
lua_pushstring(l, "val1");
lua_settable(l, -3); // stack に積んだ table に key, value を設定
lua_pushstring(l, "key2");
lua_pushstring(l, "val2");
lua_settable(l, -3); // stack に積んだ table に key, value を設定
lua_pushstring(l, "key3");
lua_pushstring(l, "val3");
lua_settable(l, -3); // stack に積んだ table に key, value を設定
if (lua_pcall(l, 1, 1, 0)) {    // 引数 1 個, 戻り値 1 個
  fprintf(stderr, "cannot exec add. %s\n", lua_tostring(l, -1));
  return;
}
show_table(l);      // 戻ってきた table の内容表示
lua_pop(l, 1);      // 戻り値を pop

stack に積まれている table へのアクセス

show_table.cpp
// stack に積まれている table の内容にひと通りアクセス
void show_table(lua_State* l)
{
  lua_pushnil(l);
  while (lua_next(l, -2)) {
    switch (lua_type(l, -2)) {  // key を表示
    case LUA_TNUMBER :
      printf("key=%td, ", lua_tointeger(l, -2));
      break;
    case LUA_TSTRING :
      printf("key=%s, ", lua_tostring(l, -2));
      break;
    }
    switch (lua_type(l, -1)) {  // value を表示
    case LUA_TNUMBER :
      printf("value=%td\n", lua_tointeger(l, -1));
      break;
    case LUA_TSTRING :
      printf("value=%s\n", lua_tostring(l, -1));
      break;
    case LUA_TBOOLEAN :
      printf("value=%d\n", lua_toboolean(l, -1));
      break;
    default :
      printf("value=%s\n", lua_typename(l, lua_type(l, -1)));
    }
    lua_pop(l, 1);      // 値を取り除く
  }
}

// 特定 key へのアクセス
void show_table_item(lua_State* l, const char* key)
{
  lua_pushstring(l, key);   // key 値を積む
  lua_rawget(l, 1);     // table のある stack index は 1 番目
  if (lua_isstring(l, -1)) {
    const char* val = lua_tostring(l, -1);
    printf("key=%s, value=%s\n", key, val);
  }
}

lua から C へ table を渡す

C の関数 c_func() に引き数として table を渡し, 戻り値として table を受け取る.

func.lua
t = {}
t["key1"] = "val1"
t["key2"] = "val2"
t["key3"] = "val3"
rt = c_func(t)
for key, val in pair(rt) do
  print(key, val)
end
call.cpp
// lua から呼ばれる関数
static int
c_func(lua_State* l)
{
  show_table(l);      // 引き数として渡された table の内容表示
  // table を戻り値として返す
  lua_newtable(l);
  lua_pushstring(l, "retkey1");
  lua_pushstring(l, "retval1");
  lua_settable(l, -3);
  lua_pushstring(l, "retkey2");
  lua_pushstring(l, "retval2");
  lua_settable(l, -3);
  return 1;          // 戻り値の数
}

// lua script を実行するコード
int main(int ac, char* av[])
{
  lua_State* l = luaL_newstate();
  luaL_openlibs(l);
  luaL_loadfile(l, "func.lua");
  lua_register(l, "c_func", c_func); // lua に関数を登録
  lua_pcall(l, 0, 0, 0); // script 実行
  lua_close(l);
}

関数群をモジュールとしてまとめる

lua から C の関数を myLib モジュールの関数として呼び出せるようにする.

func.lua
val1 = myLib.add(5, 6)
val2 = myLib.mul(5, 6)
call.cpp
// lua から呼ばれる関数
static int
l_add(lua_State* l)
{
  int x = luaL_checkint(l, -2); // 第一引数
  int y = luaL_checkint(l, -1); // 第二引数
  // pop は lua library がやってくれるので不要
  int ret = x + y;
  lua_pushnumber(l, ret);   // 戻り値を push
  return 1;         // 戻り値の数を返す
}

static int
l_mul(lua_State* l)
{
  int x = luaL_checkint(l, -2); // 第一引数
  int y = luaL_checkint(l, -1); // 第二引数
  // pop は lua library がやってくれるので不要
  int ret = x * y;
  lua_pushnumber(l, ret);   // 戻り値を push
  return 1;         // 戻り値の数を返す
}

// モジュールを登録する関数
static int luaopen_myLib(lua_State* l)
{
  static const struct luaL_Reg myLib[] = {
    {"add", l_add},
    {"mul", l_mul},
    {NULL, NULL}
  };
  // モジュールを登録. lua v5.2, 5.1 では方法が違う
#if LUA_VERSION_NUM >= 502
  luaL_newlib(l, myMathLib);
#else
  luaL_register(l, "myMath", myMathLib);
#endif
  return 1;
}

int main(int ac, char* av[])
{
  lua_State* l = luaL_newstate();
  luaL_openlibs(l);
  // myLib = require('myLib') と同等
  luaL_requiref(l, "myLib", luaopen_myLib, 1);
  luaL_loadfile(l, fname);
  if (lua_pcall(l, 0, 0, 0)) {
    fprintf(stderr, "cannot exec %s\n", fname);
    fprintf(stderr, "cannot exec add. %s\n", lua_tostring(l, -1));
    return;
  }
  lua_close(l);
}

C から lua の coroutine を制御する

下記, C の関数 c_func() の呼び出しにより実行が中断され,
C 言語側で lua_resume() することにより中断箇所から再開されるようにする.
再開時に c_func() から戻り値付きで制御が戻るようにする.

func.lua
arg1 = "arg1"
ret1 = c_func(arg1)
print("ret1=" .. ret1)
arg2 = "arg2"
ret2= c_func(arg2)
print("ret2=" .. ret2)
arg3 = "arg3"
ret3= c_func(arg3)
print("ret3=" .. ret3)
call.cpp
// 呼び出されると coroutine を中断する
static int
l_func(lua_State* l)
{
  const char* arg = luaL_checkstring(l, -1);
  printf("l_call, arg=%s\n", arg);
  return lua_yield(l, 0);
}

int main(int ac, char* av[])
{
  lua_State* l = luaL_newstate();
  luaL_openlibs(l);
  lua_register(l, "c_func", c_func); // lua に関数を登録
  lua_State* co = lua_newthread(l); // co-routine 用の thread
  luaL_loadfile(co, "func.lua");
  lua_resume(co, NULL, 0);  // 実行開始
  for (;;) {
    sleep(1);
    lua_pushstring(co, "reval"); // 戻り値を積む
    int ret = lua_resume(co, NULL, 1); // 実行再開
    printf("lua_resume returns %d\n", ret);
    if (ret == LUA_OK)      // co-routine 終了
      break;
  }
  // co-routine's thread の片付けは GC に任せる以外方法なし
  lua_close(l);
}