Cloning a Lua table in Lua C API
Asked Answered
N

2

7

There are heaps of examples of how to clone a Lua table in Lua, however I wasn't able to find any example of how to do it with the native Lua C API. I tried to do it by hand twice, but ended up with a real (although working) mess.

Does anyone have any tips or links on how to elegantly do a shallow copy of a Lua table in the C API?

Nessus answered 26/12, 2010 at 19:40 Comment(0)
M
10

What you need to do is define the Lua function, and then break it down into the associated API calls.

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

So we're going to need to take the index of a table on the stack and the 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.*/

        }
    }

Now our copied table sits on the top of the stack.

Christ, the Lua API sucks.

Missend answered 26/12, 2010 at 21:14 Comment(9)
Woah, forgot about lua_insert's swapping possibility! Thanks a lot :). And no, I don't think it sucks -- considering we can do such complex stuff using a C-level language...Nessus
Also, one should note that this will only work with an absolute index, maybe a call to lua_absindex would be in order.Nessus
@Kornel: It sucks because you can do way better with object orientation. Every step of writing every Lua interacting function, you practically have to write down what's on the stack. It would be far superior if the Lua API was object-orientated in any way except the lua_State. It's like they learned how to write decent code, and then forgot half way through.Missend
@DeadMG, how do you imagine that in C, without sacrificing performance? In C++ you always have luabind :)Nessus
@Kornel: OO access would be faster, not slower, as you don't have to find the object on the stack every time, and then perform the operation- you can just perform it straight on a pointer.Missend
@DeadMG, you can use lua_ref for that.Nessus
Remember that Lua is meant for ANSI C. It can be used anywhere from embedded devices (like the Lego Mindstorm NXT) to servers. You can always create a C++ interface built on top of the Lua API, like the one in LuaPlus, QtLua and DiluculumCrowson
@MiKy: They have an OO interface for the lua_State, why not have one instead of the stack? And, I could spend my life wrapping Lua, or I could just move on. @Kornel: lua_ref can only insert and extract from tables, it can't replace the crappy stack.Missend
Honestly, I like the stack. It's a simple, thin api where I don't have to worry about glitches in the code provided, unlike the more complex api's like Luabind where it's hard to tell wtf is going on and twice as hard to debug errors. The point is, it's a matter of preference - if you want a stack-based language, pick a stack-based one. If you want an OO language, pick an OO language.Pedagogue
S
0

HI following code segment implements 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;
}
Stipendiary answered 14/8, 2020 at 4:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.