此第一版是為 Lua 5.0 編寫的。儘管在很大程度上仍然適用於後續版本,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您同時也能贊助 Lua 專案


25.3 – 通用呼叫函數

作為一個更進階的範例,我們將使用 C 中的 vararg 設施,為呼叫 Lua 函數建立一個包裝函數。我們的包裝函數(我們稱它為 call_va)接收要呼叫的函數名稱、描述引數和結果型別的字串,然後接收引數清單,最後接收儲存結果的變數指標清單;它會處理 API 的所有細節。有了這個函數,我們可以將前一個範例簡寫成

    call_va("f", "dd>d", x, y, &z);
其中字串 "dd>d" 表示「兩個雙精度型態的引數,一個雙精度型態的結果」。此描述符可以使用字母 `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 無法從堆疊中彈出結果。在完成使用偶發字串結果(或將它們複製到其他緩衝區)後,由呼叫者彈出它們。