This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


25.3 – A Generic Call Function

As a more advanced example, we will build a wrapper for calling Lua functions, using the vararg facility in C. Our wrapper function (let us call it call_va) receives the name of the function to be called, a string describing the types of the arguments and results, then the list of arguments, and finally a list of pointers to variables to store the results; it handles all the details of the API. With this function, we could write our previous example simply as

    call_va("f", "dd>d", x, y, &z);
where the string "dd>d" means "two arguments of type double, one result of type double". This descriptor can use the letters `d´ for double, `i´ for integer, and `s´ for strings; a `>´ separates arguments from the results. If the function has no results, the `>´ is optional.
    #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);
    }
Despite its generality, this function follows the same steps of our previous example: It pushes the function, pushes the arguments, does the call, and gets the results. Most of its code is straightforward, but there are some subtleties. First, it does not need to check whether func is a function; lua_pcall will trigger any occasional error. Second, because it pushes an arbitrary number of arguments, it must check the stack space. Third, because the function may return strings, call_va cannot pop the results from the stack. It is up to the caller to pop them, after it finishes using occasional string results (or after copying them to other buffers).