【问题标题】:Simplify Lua function call to C function简化 Lua 函数对 C 函数的调用
【发布时间】:2017-03-07 15:56:11
【问题描述】:

我是 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)

?

谢谢。

【问题讨论】:

    标签: lua


    【解决方案1】:

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

    local cache = { }
    
    debug.setmetatable(obj, {
        __index = function (obj, k)
            return function (obj, ...) -- or (...) for [non-canonical] .-syntax
                return call(obj, k, ...)
            end
    
            -- malloc-optimized version --
            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) --> the same via syntactic sugar
    

    如果obj 已经有一个元表,则必须以类似的方式修改该元表。

    同样可以通过C接口完成,所以你可以将obj创建与__index设置结合起来。


    C 端更新:

    如果创建函数在外部库中,那么您别无选择,只能捕获所有对象外观点(作为参数传递的表中的返回值和值)并如上所述包装在那里。

    如果创建功能在你的控制之下,你可以在那里看到类似的东西:

    ud = lua_newuserdata(L, sizeof(object));
    *ud = object;
    
    // this part is missing if objects have no metatable at all
    luaL_getmetatable(L, tname); // or 'if (luaL_newmetatable(L, tname)) { ... }'
    lua_setmetatable(L, -2);
    
    return 1;
    

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

    ...在代码中的某处,可能就在*ud = object 行之后...

    if (luaL_newmetatable(L, tname)) {
        ... original metatable setup ...
    
        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'ed 块:

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

    然后call 将被本地化到闭包中。

    【讨论】:

    • 另请注意,每次调用此方法时都会在k 周围创建一个闭包。为了防止在紧密循环中出现这种情况,您可以缓存内部函数 (return function...) 并从缓存中返回已经看到的 ks。 (为此添加了示例代码)
    • 包装器可能应该返回 call 返回到调用站点的任何内容。
    • @greatwolf - 谢谢!固定的。此修复的注意事项:由于它现在进行尾调用,任何与级别相关的 lua 例程(例如 error 带有级别参数)应该采用级别 1,因为匿名函数(obj,...)将被替换为 @ 987654338@.
    • @user3125367 谢谢! lua 版本有效!但是,如何通过 C 接口来实现呢?由于obj是由C函数创建的,所以通过lua修改它的元表很不方便
    • @ZSaberLv0 - 为 C 更新
    猜你喜欢
    • 2021-04-15
    • 2014-09-01
    • 2015-07-14
    • 2013-02-19
    • 2015-12-30
    • 2022-01-04
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多