【问题标题】:Cloning a Lua table in Lua C API在 Lua C API 中克隆 Lua 表
【发布时间】:2011-05-30 22:14:43
【问题描述】:

有很多关于如何在 Lua 中克隆 Lua 表的示例,但是我无法找到任何使用本机 Lua C API 进行克隆的示例。我尝试过两次手动操作,但结果是一团糟(虽然有效)。

有没有人有关于如何优雅地在 C API 中对 Lua 表进行浅拷贝的提示或链接?

【问题讨论】:

    标签: c lua lua-api


    【解决方案1】:

    您需要做的是定义 Lua 函数,然后将其分解为相关的 API 调用。

    shallow_copy = function(tab)
        local retval = {}
        for k, v in pairs(tab) do
            retval[k] = v
        end
        return retval
    end
    

    所以我们需要获取堆栈上表的索引和 lua_State。

    void shallow_copy(lua_State* L, int index) {
    
    /*Create a new table on the stack.*/
    
            lua_newtable(L);
    
    /*Now we need to iterate through the table. 
    Going to steal the Lua API's example of this.*/
    
            lua_pushnil(L);
            while(lua_next(L, index) != 0) {
    /*Need to duplicate the key, as we need to set it
    (one pop) and keep it for lua_next (the next pop). Stack looks like table, k, v.*/
    
    
                lua_pushvalue(L, -2);
    /*Now the stack looks like table, k, v, k. 
    But now the key is on top. Settable expects the value to be on top. So we 
    need to do a swaparooney.*/
    
                lua_insert(L, -2);
    
        /*Now we just set them. Stack looks like table,k,k,v, so the table is at -4*/
    
    
    
        lua_settable(L, -4);
    
    /*Now the key and value were set in the table, and we popped off, so we have
    table, k on the stack- which is just what lua_next wants, as it wants to find
    the next key on top. So we're good.*/
    
            }
        }
    

    现在我们复制的表格位于堆栈的顶部。

    天啊,Lua API 很烂。

    【讨论】:

    • 哇,忘了 lua_insert 的交换可能性!非常感谢 :)。不,我不认为这很糟糕——考虑到我们可以使用 C 级语言来做如此复杂的事情......
    • 另外,应该注意的是,这仅适用于绝对索引,可能需要调用lua_absindex
    • @Kornel:这很糟糕,因为您可以在面向对象方面做得更好。编写每个 Lua 交互函数的每一步,您实际上都必须写下堆栈中的内容。如果 Lua API 除了 lua_State 以外的任何方式都是面向对象的,那就更好了。就像他们学会了如何编写像样的代码,然后就忘记了一半。
    • @DeadMG,在不牺牲性能的情况下,您如何在 C 中想象?在 C++ 中,你总是有 luabind :)
    • 老实说,我喜欢堆栈。这是一个简单、精简的 api,我不必担心提供的代码中的故障,不像像 Luabind 这样更复杂的 api,它很难判断 wtf 正在发生,并且调试错误的难度是其两倍。关键是,这是一个偏好问题——如果你想要一种基于堆栈的语言,那就选择一种基于堆栈的语言。如果您想要一种 OO 语言,请选择一种 OO 语言。
    【解决方案2】:

    HI下面的代码段实现deepcopy,enjoy:

    static int deepCopy(lua_State* L,int n,int CacheT)
    {
        int copyIndex = 0;
        switch (lua_type(L, n))
        {
        case LUA_TNIL:
            lua_pushnil(L);
            copyIndex = lua_gettop(L);
            break;
        case LUA_TBOOLEAN:
            lua_pushboolean(L, lua_toboolean(L, n));
            copyIndex = lua_gettop(L);
            break;
        case LUA_TNUMBER:
            lua_pushnumber(L, lua_tonumber(L, n));
            copyIndex = lua_gettop(L);
            break;
        case LUA_TSTRING:
            lua_pushlstring(L, lua_tostring(L, n), lua_rawlen(L, n));
            copyIndex = lua_gettop(L);
            break;
        case LUA_TLIGHTUSERDATA:
        case LUA_TUSERDATA:
            lua_pushlightuserdata(L, (void*)lua_touserdata(L, n));
            copyIndex = lua_gettop(L);
            break;
        case LUA_TFUNCTION:
            lua_pushvalue(L, n);
            copyIndex = lua_gettop(L);
            break;
        case LUA_TTHREAD:
            lua_pushvalue(L, n);
            copyIndex = lua_gettop(L);
            break;
        case LUA_TTABLE:
        {
    
                //push key
                lua_pushvalue(L, n);
                //try to get cached obj(should pop key from stack and push get value onto stack)
                int32 type = lua_gettable(L, CacheT);
                if (type == LUA_TTABLE)
                {
                    //just return
                    copyIndex = lua_gettop(L);//push 1
                }
                else 
                {
                    //pop the pushed get table return value
                    lua_pop(L, 1);
                    {
                        lua_newtable(L);
                        copyIndex = lua_gettop(L);
    
    
                        //push key
                        lua_pushvalue(L, n);
                        //push value
                        lua_pushvalue(L, copyIndex);
                        //save created table into cacheT
                        lua_settable(L, CacheT);
    
    
                        /* table is in the stack at index 't' */
                        lua_pushnil(L);  /* first key */
                        while (lua_next(L, n) != 0) {
                            /* uses 'key' (at index -2) and 'value' (at index -1) */
                            int keyIndex = lua_absindex(L, -2);//1
                            int valueIndex = lua_absindex(L, -1);//2
                            int copyedKey = deepCopy(L, keyIndex, CacheT);//3
                            int copyedValue = deepCopy(L, valueIndex, CacheT);//4
                            //push key
                            lua_pushvalue(L, copyedKey);
                            //push value
                            lua_pushvalue(L, copyedValue);
                            lua_settable(L, copyIndex);
                            /* removes 'value'; keeps 'key' for next iteration */
                            lua_pop(L, 3);
                        }
    
                        if (1 == lua_getmetatable(L, n))//try to get metatable of n(push onto stack if return 1)
                        {
                            int metaIndex = lua_gettop(L);
                            metaIndex = lua_absindex(L, -1);
                            //push 1
                            int copyedMeta = deepCopy(L, metaIndex, CacheT);//try to copy meta table push onto stack
                            lua_setmetatable(L, copyIndex);//set meta table and pop copyedMeta
                            lua_pop(L, 1);//pop lua_getmetatable pushed value
                        }
                        else
                        {
                            ;//do nothing
                        }
                    }
                }
            break;
        }
    
        }
        return copyIndex;
    }
    
    //following c++ equals lua logic like this:
    /*
    function _G.tclone(value)
        local function __tclone(value,cached)
            local copy
            local cacheT = cached or {}
            if type(value) == 'table' then
                --if has been already cloned just return handle recursive
                if nil ~= cacheT[value] then
                    copy =   cacheT[value]
                else
                    copy = {}
                    cacheT[value] = copy
                    for k,v in pairs(value) do
                        --clone key                --clone value
                        copy[__tclone(k,cacheT)] = __tclone(v,cacheT)
                    end
                    --clone metatable
                    setmetatable(copy, __tclone(getmetatable(value), cacheT))
                end
            else
                copy = value
            end
            return copy
        end
        return __tclone(value,nil)
    end
    ---
    */
    static int tClone(lua_State* L, int n)
    {
        int cacheT;
        int copy;
        lua_newtable(L);
        cacheT = lua_gettop(L);
        copy = deepCopy(L, n, cacheT);
        lua_remove(L, cacheT);
        return copy;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-30
      • 2011-02-17
      • 2013-06-17
      • 2019-10-15
      • 1970-01-01
      • 2015-05-21
      • 2015-05-31
      • 2021-06-13
      相关资源
      最近更新 更多