ククログ

株式会社クリアコード > ククログ > LuaのC APIを使ってネストしたテーブル型のデータを作成するには

LuaのC APIを使ってネストしたテーブル型のデータを作成するには

はじめに

Luaのデータ型の一つにテーブル型があり、ネストさせることができます。 今回は、ネストしたテーブル型のデータをLuaのC APIを使って作成する方法を紹介します。

2024/09/12追記: 記事内のサンプルコードに以下のような不適切な箇所があるとの指摘を受けて修正いたしました。
  • lua_setglobal()を呼ぶ際に、誤ってキーに型名であるtableを指定してしまっていた(スタックの扱いが不適切)
  • 後始末のためのlua_close(L)を呼び出していない

ネストしていないテーブル型のデータを作成するには

いきなりネストしたテーブルを作成する方法を説明するとややここしくなるので、先にネストしていないテーブル型の場合を説明します。

Luaでprint(inspect(animal))した結果、次のように表示されるテーブルをC APIを使って作成します。1

{
  cat = "Meow",
  dog = "Bow"
}

このテーブルは次のようなサンプルコードで作成できます。

#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

int main(void)
{
  lua_State *L = luaL_newstate();

  luaL_openlibs(L);
  lua_newtable(L);

  lua_pushstring(L, "Meow");
  lua_setfield(L, -2, "cat");

  lua_pushstring(L, "Bow");
  lua_setfield(L, -2, "dog");

  lua_setglobal(L, "animal");
  luaL_dostring(L, "local inspect=require('inspect'); print(inspect(animal))");

  lua_close(L);
  return 0;
}

コンパイル後のバイナリを実行すると、標準出力に次のようにanimalテーブルの内容が表示されます。

{
  cat = "Meow",
  dog = "Bow"
}

上記のサンプルは、Cでテーブルを作成して、Luaスクリプトでそのテーブルを参照して表示します。

Luaはスタックを使ってCとデータをやりとりします。そのため、次のような流れで処理する必要があります。

  • lua_newtable(L);でテーブル作成してスタックに積む

  • lua_pushstring(L, "Meow");でスタックに文字列"Meow"を積む

  • lua_setfield(L, -2, "cat");でキーを"cat"、値をスタックに積んである"Meow"としてスタックに積んであるテーブルに設定する

lua_setfieldを呼ぶと、スタックに積まれた"Meow"は除去されます。 -2 はスタックのトップを0としたインデックスを意味します。 つまり、テーブル、"Meow"の順にスタックに積み上げているので、-1は"Meow"を、-2はテーブルを意味します。

ここまでを図示すると次のようになります。

setfieldの例

setfieldを使わない場合(参考)

テーブルを作成するサンプルでは、lua_setfieldを使わずに、lua_settableを使っていることもあります。 lua_settableの場合は、キーと値を積んだ状態で呼びだします。例えば、次のような使い方をします。

  lua_newtable(L);

  lua_pushstring(L, "cat");
  lua_pushstring(L, "Meow");

  lua_settable(L, -3);

lua_settable(L, -3);ではキーが"cat"、"Meow"が値としてスタックに積んだテーブルに設定します。 テーブル、"cat"、Meow"の順にスタックに積み上げているので、-1は"Meow"を、-2は"cat"を、-3はテーブルを意味します。

lua_settableの場合も同様に図示すると次のようになります。

settableの例

ネストしているテーブル型のデータを作成するには

ネストしていないテーブル型のデータの作成方法がわかったところで、本題のネストしたテーブルを作成する方法を説明します。

Luaでprint(inspect(sample))した結果、次のように表示されるテーブルを作成します。

{
  animal = {
    cat = "Meow"
  }
}

このテーブルは次のようなサンプルコードで作成できます。

#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

int main(void)
{
  lua_State *L = luaL_newstate();
  int status = 0;

  luaL_openlibs(L);

  lua_newtable(L);
  lua_pushstring(L, "animal");

  lua_newtable(L);
  lua_pushstring(L, "cat");
  lua_pushstring(L, "Meow");

  lua_settable(L, -3);
  lua_settable(L, -3);

  lua_setglobal(L, "sample");

  luaL_dostring(L, "inspect=require('inspect'); print(inspect(sample))");
  lua_close(L);
  return 0;
}
  • lua_newtable(L);でテーブルを作成してスタックに積む

  • lua_pushstring(L, "animal");でスタックに文字列"animal"を積む

  • lua_newtable(L);でネストしているテーブルを作成してスタックに積む

  • lua_pushstring(L, "cat");でネストしているテーブルのキーとなる"cat"をスタックに積む

  • lua_pushstring(L, "Meow");でネストしているテーブルの値となる"Meow"をスタックに積む

  • lua_settable(L, -3);でスタックからキー(cat)と値(Meow)をとりだし、ネストしているテーブルに設定する

  • lua_settable(L, -3);(2つめ)でスタックからキー"animal"と値(ネストしたテーブル)をとりだし、テーブルに設定する

この場合も同様に図示すると次のようになります。

ネストしたテーブル作成その1

ネストしたテーブルの作成その2

ポイントは、キーである"animal"の値としてネストしたテーブルをlua_settableしていることです。 このようにすることで、多段にネストしたテーブル型のデータを作成できます。

まとめ

今回は、ネストしたテーブル型のデータをLuaのC APIを使って作成する方法を紹介しました。 Luaの言語バインディングを書く機会があったら参考にしてみてください。

  1. inspectはluarocks install inspectでインストールしているものとします。