从 C API 确定 Lua 函数的签名

我正在开发的一个框架可能会用 Lua 模块进行扩展。每个模块的 Lua 源代码都是用我们基于官方 Lua 解释器的编译器编译的,然后保存为字节码。这些模块必须符合一定的要求:

-- 必须是一个非空字符串,只能包含小写字母
name = "foo"

-- 必须不仅是一个数字,而且还必须是一个大于零的整数
version = 1

如果在将 Lua 源代码编译成模块时可以检查要求是否满足,那就太好了:

  • 对于编写模块的人来说,这将使他们知道自己犯了什么错误;
  • 对于我们来说,我们可以假定模块是正确的(就像我们假定已安装的资源(如图标)是正确的)因此无需在运行时实现任何检查。

检查某个值是否为某种类型并不困难:

// lua_getglobal 返回值的类型
int r = lua_getglobal(lua_state, "name");
if ( r == LUA_TSTRING )
{
    // 干得好,亲爱的模块编写者(仍必须检查字符串是否只包含
    // 有效字符)
}
else if ( r == LUA_TNIL )
{
    // 错误:`name' 未定义
}
else
{
    // 嘿你,“name”应该是一个字符串!
}

但是如何检查一个函数需要多少个参数,并返回一张带有特定字段的表格呢?

-- 必须用两个参数定义
function valid_function( arg1 , arg2 )
    -- 必须返回一张表格
    return {
        a = 17, -- 带有字段'a',一个数字
        b = "a" -- 带有字段'b',一个字符串
   }
end

请注意,我想知道是否可以使用 C API 实现这个功能(如果可以,那么怎么实现),而不是像 这个问题 那样从 Lua 内部实现。

点赞
用户90511
用户90511

你不能这么做。每个 Lua 函数都可以接受任意数量的参数,并返回任意类型的任意数量的值。

我认为最好的方法是在某个地方写上显式的类型注释(可以是注释或者运行时代码),然后进行检查。另外还有一个带有静态类型系统的实验性 Lua 方言——Typed Lua

举个例子,函数可以接受任意数量的参数。如果函数接收到的参数比它的形参少,多出来的形参将被赋值为 nil。如果传入的参数比函数的形参更多,多余的参数会被丢弃。

function foo(x, y)
    print(x,y)
end

foo()      -- 打印出 nil, nil
foo(1)     -- 打印出 1,   nil
foo(1,2)   -- 打印出 1,   2
foo(1,2,3) -- 打印出 1,   2

缺失的参数也常常被用来实现带有可选参数的函数,这意味着尝试检查参数数量的天真方法会与这个常见的 Lua 习惯用法冲突:

function hello(name)
    name = "mysterious stranger"
    print("Hello, "..name.."!")
end

返回值也像输入参数一样灵活:

function bar()
    return 1, 2
end

local x       = bar() -- 第二个返回值被丢弃
local x, y    = bar()
local x, y, z = bar() -- z 被赋值为 `nil`

很多 Lua 函数还根据条件返回不同数量的值。例如,io.open 返回一个值(文件句柄)如果它能够打开文件,或者返回两个值(nil 加上一个错误消息),如果它无法打开文件。

2016-09-04 15:48:53