在 Lua C API 中克隆 Lua 表

7

有很多关于如何在Lua中克隆Lua表的示例,但是我找不到使用本地Lua C API进行操作的示例。我曾经手动尝试过两次,但最终得到的结果非常混乱(虽然可以正常工作)。

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

2个回答

10

你需要做的是定义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 真糟糕。


1
哇,忘记了 lua_insert 的交换功能!非常感谢 : )。而且,我并不认为它很糟糕——考虑到我们可以使用 C 级别的语言来完成如此复杂的事情... - Kornel Kisielewicz
此外,需要注意的是,这仅适用于绝对索引,也许需要调用 lua_absindex - Kornel Kisielewicz
1
@Kornel:这很糟糕,因为你可以通过面向对象的方式做得更好。编写每个与Lua交互的函数时,你几乎必须写下堆栈上的内容。如果Lua API在除了lua_State之外的任何方面都是面向对象的,那将会更加优秀。就像他们学会了如何编写良好的代码,然后在半路上忘记了一样。 - Puppy
@Kornel:面向对象的访问速度会更快,因为您不必每次都在堆栈上查找对象,然后执行操作-您可以直接在指针上执行操作。 - Puppy
3
说实话,我喜欢这个堆栈。它是一个简单、轻量级的API,我不用担心代码中的故障,而与Luabind等更复杂的API不同,在那里很难知道正在发生什么,并且调试错误要难两倍。重点是,这是一种偏好 - 如果你想要一个基于堆栈的语言,就选择一个基于堆栈的语言。如果你想要一个面向对象的语言,就选择一个面向对象的语言。 - Ponkadoodle
显示剩余4条评论

0

嗨,以下代码段实现了深拷贝,请享用:

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接