25.3 通用的函数调用

看一个稍微高级的例子,我们使用Cvararg来封装对Lua函数的调用。我们的封装后的函数(call_va)接受被调用的函数明作为第一个参数,第二参数是一个描述参数和结果类型的字符串,最后是一个保存返回结果的变量指针的列表。使用这个函数,我们可以将前面的例子改写为:

call_va("f", "dd>d", x, y, &z);

字符串 "dd>d" 表示函数有两个double类型的参数,一个double类型的返回结果。我们使用字母 'd' 表示double'i' 表示integer's' 表示strings'>' 作为参数和结果的分隔符。如果函数没有返回结果,'>' 是可选的。

#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不能从栈中弹出结果,在调用者获取临时字符串的结果之后(拷贝到其他的变量中),由调用者负责弹出结果。

 

 



相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛