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


26.1 – C 関数

最初の例として、特定の番号のサインを返す関数の簡易バージョンを実装する方法を説明しましょう(より適切な実装では、引数が数値であるかどうかを確認する必要があります)。

    static int l_sin (lua_State *L) {
      double d = lua_tonumber(L, 1);  /* get argument */
      lua_pushnumber(L, sin(d));  /* push result */
      return 1;  /* number of results */
    }
Lua で登録されたどの関数も、lua.hlua_CFunction として定義されたこの同じプロトタイプを持つ必要があります。
    typedef int (*lua_CFunction) (lua_State *L);
C の観点から見ると、C 関数は Lua の状態を単一の引数として受け取り、返される値の数を含む整数を C で返します(Lua)。したがって、この関数は結果をプッシュする前にスタックをクリアする必要はありません。関数が返されると、Lua は結果の下にスタックに存在するものすべてを自動的に削除します。

この関数を Lua で使用するには、登録する必要があります。この処理を lua_pushcfunction を使用して行います。これは C 関数へのポインタを取得し、Lua 内でこの関数を表す「関数」型の値を作成します。l_sin をテストする簡単かつ手短な方法は、そのコードをファイル lua.c に直接配置し、lua_open への呼び出しの直後に次の行を追加することです。

    lua_pushcfunction(l, l_sin);
    lua_setglobal(l, "mysin");
最初の行は関数型の値をプッシュします。2 番目の行はそれをグローバル変数 mysin に割り当てます。これらの変更を行ったら、Lua 実行可能ファイルを再構築します。そうすると、Lua プログラムで新しい関数 mysin を使用できます。次のセクションでは、新しい C 関数を Lua とリンクするためのより優れた方法について説明します。

より高度なサイン関数の場合、その引数の型を確認する必要があります。ここでは、補助ライブラリが役立ちます。luaL_checknumber 関数は、特定の引数が数値かどうかを確認します。エラーが発生した場合は、情報を提供するエラーメッセージをスローします。それ以外の場合は、数値を返します。関数の変更は最小限です。

    static int l_sin (lua_State *L) {
      double d = luaL_checknumber(L, 1);
      lua_pushnumber(L, sin(d));
      return 1;  /* number of results */
    }
上記の定義で mysin('a') を呼び出すと、次のメッセージが表示されます。
    bad argument #1 to `mysin' (number expected, got string)
luaL_checknumber が引数の番号 (1)、関数名(「mysin」)、予期されるパラメータタイプ(「number」)、および実際のパラメータタイプ(「string」)をメッセージに自動的に埋め込むことに注意してください。

より複雑な例として、特定のディレクトリの内容を返す関数を記述しましょう。Lua はこの関数を標準ライブラリでは提供していません。ANSI C にはこのジョブ用の関数がないためです。ここでは、POSIX に準拠したシステムがあると仮定します。私たちの関数 dir は、ディレクトリのパスを含む文字列を引数として取得し、ディレクトリエントリを含む配列を返します。たとえば、dir("/home/lua") を呼び出すと、テーブル {".", "..", "src", "bin", "lib"} が返される可能性があります。エラーが発生した場合は、関数は nil とエラーメッセージを含む文字列を返します。

    #include <dirent.h>
    #include <errno.h>
    
    static int l_dir (lua_State *L) {
      DIR *dir;
      struct dirent *entry;
      int i;
      const char *path = luaL_checkstring(L, 1);
    
      /* open directory */
      dir = opendir(path);
      if (dir == NULL) {  /* error opening the directory? */
        lua_pushnil(L);  /* return nil and ... */
        lua_pushstring(L, strerror(errno));  /* error message */
        return 2;  /* number of results */
      }
    
      /* create result table */
      lua_newtable(L);
      i = 1;
      while ((entry = readdir(dir)) != NULL) {
        lua_pushnumber(L, i++);  /* push key */
        lua_pushstring(L, entry->d_name);  /* push value */
        lua_settable(L, -3);
      }
    
      closedir(dir);
      return 1;  /* table is already on top */
    }
補助ライブラリから luaL_checkstring 関数は、文字列の luaL_checknumber と同等です。

(極端な状況下では、l_dir のそのインプリメンテーションは小さなメモリリークの原因となる可能性があります。呼び出される Lua 関数の 3 つは、メモリ不足により失敗する可能性があります: lua_newtablelua_pushstring、および lua_settable。これらの呼び出しのどれかが失敗すると、エラーが発生して l_dir が中断されるため、それによって closedir は呼び出されません。前述のとおり、ほとんどのプログラムではこれは大きな問題ではありません。プログラムのメモリが不足した場合、最善の方法はとにかくシャットダウンすることです。それにもかかわらず、第 29 章 では、この問題を回避するディレクトリ関数の代替実装を紹介します。)