第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但有一些区别。
第四版针对 Lua 5.3,可以在 Amazon 和其他书店买到。
购买本书,您还可以帮助支持 Lua 项目。
![]() |
用 Lua 编程 | ![]() |
| 第四部分。C API 第 25 章。扩展您的应用程序 |
作为一个更高级的示例,我们将构建一个用于调用 Lua 函数的包装器,使用 C 中的 vararg 工具。我们的包装器函数(我们称之为 call_va)接收要调用的函数的名称、描述参数和结果类型的字符串、然后是参数列表,最后是用于存储结果的变量指针列表;它处理 API 的所有详细信息。使用此函数,我们可以简单地将我们之前的示例编写为
call_va("f", "dd>d", x, y, &z);
其中字符串 "dd>d" 表示“两个 double 类型的参数,一个 double 类型的结果”。此描述符可以使用字母 `d´ 表示 double,`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 无法从堆栈中弹出结果。在完成使用偶发字符串结果(或将其复制到其他缓冲区)后,由调用者弹出它们。
| 版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 | ![]() |