luaのグローバル変数をCで使う

ソフトウェアの設定をスクリプトで置き換えれば、色々パラメータを変えてシミュレーションをしたいなどという場合に大変便利。たとえば以下のような人物に関する情報を一つのファイルに格納しているような場合

-- data.lua
name="YAMADA Taro"
age=25
height=170.0
checked=false


これを読み出して利用するコードは以下のように書ける。

#include <string.h>
#include <assert.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#define NAME_LENGTH 256
#define MAX(a,b) ( (a)>(b) ? (a):(b) )

struct Person {
  char		name[NAME_LENGTH];
  int		age;
  double	height;
  int		checked;
};

int loadPersonFromFile(struct Person *config, const char *fname);

int main(int argc, char* argv[])
{
  struct Person config;

  if ( loadPersonFromFile(&config, argv[1])<0 )
    {
      return 0;
    }

  printf("name    : %s\n",  config.name );
  printf("age     : %d\n",  config.age );
  printf("height  : %lf\n", config.height );
  printf("checked : %s\n",  (config.checked)? "checked": "unchecked" );

  return 0;
}

int loadPersonFromFile(struct Person	*config,
		       const char	*fname)
{
  int		 retval = 0;
  const char	*s;
  size_t	 l;

  lua_State	*L	= luaL_newstate();

  if ( luaL_loadfile(L, fname) || lua_pcall(L, 0,0,0) )
    {
      fprintf(stderr, "cannnot load config file : %s\n", lua_tostring(L, -1));
      retval = -1;
      goto done;
    }

  lua_getglobal(L, "name");
  lua_getglobal(L, "age");
  lua_getglobal(L, "height");
  lua_getglobal(L, "checked");

  assert ( lua_isstring (L, -4) );
  assert ( lua_isnumber (L, -3) );
  assert ( lua_isnumber (L, -2) );
  assert ( lua_isboolean(L, -1) );

  s		  = lua_tolstring(L, -4, &l);
    assert(s[l]=='\0');
    assert(strlen(s) <= l);
    strncpy(config->name, s, MAX(NAME_LENGTH, l));
  config->age     = lua_tointeger(L, -3);
  config->height  = lua_tonumber (L, -2);
  config->checked = lua_toboolean(L, -1);

 done:
  lua_settop(L, 0); //スタックを消去 この例ではあまり意味がない
  lua_close(L);
  return retval;
}

実行結果

% ./a.out data.lua                         
name    : YAMADA Taro
age     : 25
height  : 170.000000
checked : unchecked