如何在 Lua C API 中使用 `debug.getinfo(f).nparams`?

我了解到可以使用 debug.getinfo(f).nparams 在 Lua 中找出一个函数需要多少个参数。

请问在使用 lua_pcall() 调用函数之前如何在 Lua C API 中使用它?

这是我的样例代码,用于调用 Lua 函数。

void doFunction(lua_State *L, int argc, t_atom *argv)
{
    lua_getglobal(L, "require");
    lua_pushstring(L, "foo"); //require("foo")
    if (lua_pcall(L, 1, LUA_MULTRET, 0))
    {
        error("error: %s", lua_tostring(L, -1));
        lua_pop(L, 1);
        return;
    }
    int top = lua_gettop(L);
    lua_getfield(L, -1, "test"); //find foo.test() function
    if (lua_type(L, -1) != LUA_TFUNCTION)
    {
        lua_pop(L, 2);
        return;
    }
    lua_newtable(L);
    for (int i = 0; i < argc; ++i) //convert array to lua table
    {
        lua_pushinteger(L, static_cast<lua_Integer>(i + 1));
        if (argv[i].a_type == A_FLOAT)
            lua_pushnumber(L, static_cast<lua_Number>(argv[i].a_w.w_float));
        else if (argv[i].a_type == A_SYMBOL)
            lua_pushstring(L, argv[i].a_w.w_symbol->s_name);
        lua_settable(L, -3);
    }
    if (lua_pcall(L, 1, LUA_MULTRET, 0)) //call foo.test() function
    {
        error("error: %s", lua_tostring(L, -1));
        lua_pop(L, 1);
        return;
    }
    if (lua_gettop(L) - top)
    {
        //do something with the returned value
    }
    lua_pop(L, 1);
}

这段代码尝试 require foo 并调用 test 函数,使用提供给 doFunction 函数的参数数组。

我了解到,可以通过将以下代码正确放置到某个位置来找出一个函数期望有多少个参数:

 lua_Debug ar;
 lua_getinfo(L, ">u", &ar);
 printf("%d\n", ar.nparams);

但我不知道在我的代码中应该将这些行放在哪里才能使其正常工作。

请问有人能教我如何操作吗?


补充信息:

我希望能够通过传递表格或参数值来调用函数,具体是否传递表格取决于函数期望的参数数量。例如,如果函数期望一个参数,我将向该函数传递一个表格。但是如果函数期望多个参数,我将想要将值作为多个参数传递。这就是我需要使用 debug.getinfo(f).nparams 的原因。

点赞
用户3677376
用户3677376

Lua手册介绍如何获取有关函数的信息:

要获取有关函数的信息,您需要将其推入堆栈并从“>”字符开始使用what字符串。(在这种情况下,lua_getinfo会从堆栈的顶部弹出函数。)例如,要知道函数f在哪一行定义的,可以编写以下代码:

lua_Debug ar;
lua_getglobal(L, "f");  /* get global 'f' */
lua_getinfo(L, ">S", &ar);
printf("%d\n", ar.linedefined);

因此,您唯一需要的就是将要检查的函数值推到 Lua 堆栈的顶部。

这里有实现一个 C 函数的示例,该函数调用作为第一个参数传递的函数以其余参数为参数--作为数组或单独参数,具体取决于期望参数的数量:

/* 在 Ubuntu Linux 上构建 `args.c` 的指令:
 * `gcc -Wall -I/usr/include/lua5.2 -fpic -shared -o args.so args.c`
 */
#include <lua.h>
#include <lauxlib.h>

/* 将 Lua 堆栈区间复制到数组 */
static void push_pack(lua_State* L, int first, int last) {
  int i;
  first = lua_absindex(L, first);
  last = lua_absindex(L, last);
  lua_createtable(L, last > first ? last - first : 0, 0);
  for (i = first; i <= last; ++i) {
    lua_pushvalue(L, i);
    lua_rawseti(L, -2, i-first+1);
  }
}

static int args(lua_State* L) {
  lua_Debug ar;
  /* 第一个参数必须是要调用的函数,剩余的是该函数的附加参数 */
  luaL_checktype(L, 1, LUA_TFUNCTION);
  /* 查询期望参数的数量 */
  lua_pushvalue(L, 1);
  lua_getinfo(L, ">u", &ar);
  if (ar.nparams > 1 || ar.isvararg) { /* 分别作为多个参数传递 */
    lua_call(L, lua_gettop(L)-1, LUA_MULTRET);
    return lua_gettop(L);
  } else { /* 创建数组并作为单个参数传递 */
    int top = lua_gettop(L);
    lua_pushvalue(L, 1); /* 首先推入函数 */
    push_pack(L, 2, top); /* 然后推入参数数组 */
    lua_call(L, 1, LUA_MULTRET);
    return lua_gettop(L) - top;
  }
}

int luaopen_args(lua_State* L) {
  lua_pushcfunction(L, args);
  return 1;
}

在 Lua 5.2 上测试:

#!/usr/bin/lua5.2

local args = require("args")

local function f(x, ...)
  print(">", x, ...)
  return 5, 6
end

local function g(x)
  print(">", x)
  for i,v in ipairs(x) do
    print("", i, v)
  end
  return 7, 8
end

local function h(x, y)
  print(">", x, y)
  return 9, 10
end

print(args(f, 1, 2, 3))
print(args(g, 1, 2, 3))
print(args(h, 1, 2, 3))
print(args(print, 1, 2, 3))
2019-06-09 08:52:01