この初版は Lua 5.0 向けに書かれました。その大部分は依然として後続のバージョンに関連がありますが、若干の違いがあります。
第 4 版は Lua 5.3 を対象としており、Amazon およびその他の書店で購入できます。
本を購入することにより、Lua プロジェクトのサポートにも協力していただけます。


25.3 –汎用呼び出し関数

より高度な例として、C の vararg 機能を使用して Lua 関数を呼び出すラッパーを構築します。当社のラッパー関数 (call_va と呼びましょう) は、呼び出される関数の名前、引数と結果の型を表す文字列、引数のリスト、最後に結果を格納するための変数へのポインターのリストを受け取ります。ラッパー関数は API のすべての詳細を処理します。この関数を使用すると、前述の例を次のように簡単に記述できます。

    call_va("f", "dd>d", x, y, &z);
文字列 "dd>d" は「2 つの double 型の引数と 1 つの double 型の結果」を表します。この記述子は、double には `d´、整数には `i´、文字列には `s´ を使用できます。`>´ は引数と結果を区切ります。関数が結果を持たない場合、`>´ は省略できます。
    #include <stdarg.h>
    
    void call_va (const char *func, const char *sig, ...) {
      va_list vl;
      int narg, nres;  /* number of arguments and results */
    
      va_start(vl, sig);
      lua_getglobal(L, func);  /* get function */
    
      /* push arguments */
      narg = 0;
      while (*sig) {  /* push arguments */
        switch (*sig++) {
    
          case 'd':  /* double argument */
            lua_pushnumber(L, va_arg(vl, double));
            break;
    
          case 'i':  /* int argument */
            lua_pushnumber(L, va_arg(vl, int));
            break;
    
          case 's':  /* string argument */
            lua_pushstring(L, va_arg(vl, char *));
            break;
    
          case '>':
            goto endwhile;
    
          default:
            error(L, "invalid option (%c)", *(sig - 1));
        }
        narg++;
        luaL_checkstack(L, 1, "too many arguments");
      } endwhile:
    
      /* do the call */
      nres = strlen(sig);  /* number of expected results */
      if (lua_pcall(L, narg, nres, 0) != 0)  /* do the call */
        error(L, "error running function `%s': %s",
                 func, lua_tostring(L, -1));
    
      /* retrieve results */
      nres = -nres;  /* stack index of first result */
      while (*sig) {  /* get results */
        switch (*sig++) {
    
          case 'd':  /* double result */
            if (!lua_isnumber(L, nres))
              error(L, "wrong result type");
            *va_arg(vl, double *) = lua_tonumber(L, nres);
            break;
    
          case 'i':  /* int result */
            if (!lua_isnumber(L, nres))
              error(L, "wrong result type");
            *va_arg(vl, int *) = (int)lua_tonumber(L, nres);
            break;
    
          case 's':  /* string result */
            if (!lua_isstring(L, nres))
              error(L, "wrong result type");
            *va_arg(vl, const char **) = lua_tostring(L, nres);
            break;
    
          default:
            error(L, "invalid option (%c)", *(sig - 1));
        }
        nres++;
      }
      va_end(vl);
    }
この関数は汎用的ですが、前述の例のステップと同様の手順に従います。関数をプッシュし、引数をプッシュし、呼び出しを行い、結果を取得します。コードの大部分は単純ですが、微妙な部分があります。まず、func が関数かどうかをチェックする必要はありません。lua_pcall は、時折発生するエラーをトリガーします。第二に、任意の数の引数をプッシュするため、スタック領域をチェックする必要があります。第三に、関数が文字列を返す可能性があるため、call_va はスタックから結果をポップできません。呼び出し側は、時折発生する文字列の結果を使用し終えた後 (または別のバッファーにコピーした後) に結果をポップする必要があります。