简化 Lua 函数调用为 C 函数

我是 Lua 的新手

我已经向 Lua 注册了一个 C 函数,它看起来像这样:

call(obj, "func", param0, param1)

call() 中:

  • 我们通过 C++ 中的自定义反射逻辑进行一些复杂的函数调用
  • obj (和所有参数) 都是固定的包装类型 (类似于 boost::any)

因此,我们无法使用常规的 Lua 绑定工具 (luabind、luabridge 等) 进行简单的类成员注册

这是问题:

我们如何简化 Lua 调用,使其像这样:

obj.func(param0, param1)

obj:func(param0, param1)

谢谢。

点赞
用户3125367
用户3125367

你必须在每个你创建的obj上设置一个元表(假设它是你控制的用户数据,并且没有元表),并在其中覆盖__index元方法:

local cache = { }

debug.setmetatable(obj, {
    __index = function (obj, k)
        return function (obj, ...) -- 或者(...)用于非规范的.-语法
            return call(obj, k, ...)
        end

        -- 优化空间分配的版本 --
        local f = cache[k] or function (obj, ...) return call(obj, k, ...) end
        cache[k] = f
        return f
    end,
    __metatable = "whatever",
})

f = obj.func
f(obj, param0, param1) --> call(obj, 'func', param0, param1)

obj:func(param0, param1) --> 同样可以使用语法糖

如果obj已经有元表,那么必须类似地修改该元表。

同样可以通过C接口完成相同的操作,因此您可以将obj的创建与__index设置结合起来。


C端的更新:

如果创建函数位于外部库中,则您除了在所有对象出现点(返回值和传递为参数的表中的值)处进行包装,以如上所述的方式外,没有其他选择。

如果创建函数由您控制,则您可能会在其中看到以下内容:

ud = lua_newuserdata(L, sizeof(object));
*ud = object;

// 如果对象根本没有元表,则此部分省略
luaL_getmetatable(L, tname); // 或 'if (luaL_newmetatable(L, tname)) { ... }'
lua_setmetatable(L, -2);

return 1;

您必须在创建元表的地方添加__index元方法:

...代码的某个地方,可能在*ud = object行之后...

if (luaL_newmetatable(L, tname)) {
    ...设置原始元表...

    int res = luaL_loadstring(L,
        "return function (obj, k)\n"
        "    return function (obj, ...)\n"
        "        return call(obj, k, ...)\n"
        "    end\n"
        "end\n");
    assert(res == 0);

    lua_call(L, 0, 1);
    lua_setfield(L, -2, "__index");
}

如果用户数据根本没有元表,则必须创建并设置它。

如果您想摆脱全局符号call,则传递其C实现作为luaL_loadstring的参数:

luaL_loadstring(L, "local call = ...\n return function (obj, k)\n" ...);
lua_pushcfunction(L, l_call);
lua_call(L, 1, 1); // 而不是(0, 1)

然后call将被限定在闭包中。

2017-03-07 16:18:53