この初版はLua 5.0向けに書かれました。後のバージョンでも大部分が関連していますが、いくつか違いがあります。
第4版はLua 5.3を対象にしており、Amazonやその他の書店で購入できます。
この本を購入すると、Luaプロジェクトのサポートにも役立ちます。


25 – アプリケーションの拡張

Luaはコンフィギュレーション言語として利用することが重要な用途の一つです。この章では、簡単な例から始め、より複雑なタスクを実行するように進化させた、プログラムをLuaでどのように設定できるかを説明します。

最初のタスクとして、単純なコンフィギュレーションのシナリオを考えてみましょう。Cプログラム(ここではppと呼びます)にウィンドウがあり、ユーザーに初期ウィンドウサイズを指定できるようにしたいと思います。明らかに、このような簡単なタスクには環境変数や名前-値ペアを含むファイルなど、Luaを使用するよりも簡単なオプションがいくつかあります。しかし、単純なテキストファイルを使用する場合でも、それらのファイルを解析する必要があります。そのため、Luaコンフィギュレーションファイルを使用することにします(Luaプログラムであるプレーンテキストファイルです)。このファイルは最も単純な形で、次のような行を含めることができます。

    -- configuration file for program `pp'
    -- define window size
    width = 200
    height = 300

次に、Lua APIを使用して、Luaがこのファイルを解析し、グローバル変数widthheightの値を取得するように指示する必要があります。次の関数がその役割を果たします。

    #include <lua.h>
    #include <lauxlib.h>
    #include <lualib.h>
    
    void load (char *filename, int *width, int *height) {
      lua_State *L = lua_open();
      luaopen_base(L);
      luaopen_io(L);
      luaopen_string(L);
      luaopen_math(L);
    
      if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
        error(L, "cannot run configuration file: %s",
                 lua_tostring(L, -1));
    
      lua_getglobal(L, "width");
      lua_getglobal(L, "height");
      if (!lua_isnumber(L, -2))
        error(L, "`width' should be a number\n");
      if (!lua_isnumber(L, -1))
        error(L, "`height' should be a number\n");
      *width = (int)lua_tonumber(L, -2);
      *height = (int)lua_tonumber(L, -1);
    
      lua_close(L);
    }
最初に、Luaパッケージを開き、標準ライブラリをロードします(それらはオプションですが、通常は用意しておいたほうがよいでしょう)。次に、luaL_loadfileを使用してファイルfilenameからチャンクをロードし、lua_pcallを呼び出して実行します。これらの関数のいずれかでエラーが発生した場合(例:コンフィギュレーションファイルの構文エラー)、この呼び出しはゼロ以外のエラーコードを返し、エラーメッセージをスタックにプッシュします。いつものように、私たちのプログラムはlua_tostringをインデックス-1と共に使用して、スタックの一番上からメッセージを取得します。(error関数はセクション24.1で定義しました。)

チャンクの実行後、プログラムはグローバル変数の値を取得する必要があります。そのため、2回lua_getglobalを呼び出します。唯一のパラメータ(普遍的に存在するlua_State以外)は変数名です。各呼び出しは対応するグローバル値をスタックの一番上にプッシュするので、幅はインデックス-2になり、高さがインデックス-1(一番上)になります。(スタックは以前は空だったので、一番下からインデックスを付けることもでき、最初の値からは1、2番目の値からは2を使用できます。ただし、上からインデックスを付けることで、スタックが空でない場合であってもコードが機能します。)次に、この例ではlua_isnumberを使用して、各値が数値であるかどうかを確認します。その後、lua_tonumberを使用してそのような値をdoubleに変換し、Cはintへの強制変換を行います。最後に、Lua状態を閉じ、戻ります。

Luaを使用する価値はあるでしょうか?前述したように、このような簡単なタスクでは、2つの数字のみを含む単純なファイルの方がLuaを使用するよりもはるかに簡単になります。それでも、Luaを使用するとメリットがあります。まず、Luaは構文の細部(およびエラー)をすべて処理します。コンフィギュレーションファイルにはコメントを入れることさえできます。第二に、ユーザーはすでにそれを使用してより複雑な設定を行うことができます。たとえば、スクリプトはユーザーにいくつかの情報を要求したり、環境変数を照会して適切なサイズを選択したりできます。

    -- configuration file for program `pp'
    if getenv("DISPLAY") == ":0.0" then
      width = 300; height = 300
    else
      width = 200; height = 200
    end
こうしたシンプルな構成シナリオでも、ユーザーのニーズを予測するのは困難です。しかしながら、スクリプトが変数を 2 つ定義している限り、C アプリケーションは変更なしで動作します。

Luaを使用する最後の理由は、プログラムへの新しい設定機能の追加が容易になったことです。この容易さは、より柔軟なプログラムという結果につながる考え方をもたらします。