2

First of all, I appologize for not uploading the full code.

I'm trying to convert userdata to a pointer so it can be passed to other lua chunk using lua_rawgeti().

If you see outletRet() function, it checks the type of a returned value, and if it's a userdata, it passes the pointer to other lua chunk by calling outlet_pointer() and it seems to work fine.

And if userdata is inside a table, outletTable() function is called. And if one of its elements is a userdata it also converts userdata to a pointer and then pass it to other chunk by calling outlet_pointer().

However, when luaL_ref(L, LUA_REGISTRYINDEX) is called, it seems like it gets a reference to the whole table and not just userdata inside it.

How can I get the reference to the userdata and not the whole table?

void ofLua::outletTable() //called from outletRet() below
{
    lua_pushvalue(L, -1);
    lua_pushnil(L);
    int ac = 0;
    t_atom *av = static_cast<t_atom *>(getbytes(sizeof(t_atom) * ac));
    while (lua_next(L, -2))
    {
        av = static_cast<t_atom *>(resizebytes(av, sizeof(t_atom) * ac,
                                               sizeof(t_atom) * (ac + 1)));
        if (lua_isboolean(L, -1))
        {
            av[ac].a_type = A_FLOAT;
            av[ac].a_w.w_float = static_cast<t_float>(lua_toboolean(L, -1));
        }
        else if (lua_isnumber(L, -1))
        {
            av[ac].a_type = A_FLOAT;
            av[ac].a_w.w_float = static_cast<t_float>(lua_tonumber(L, -1));
        }
        else if (lua_isstring(L, -1))
        {
            av[ac].a_type = A_SYMBOL;
            av[ac].a_w.w_symbol = gensym(lua_tostring(L, -1));
        }
        else if (lua_isuserdata(L, -1))
        {
            av[ac].a_type = A_POINTER;
        }
        ac++;
        lua_pop(L, 1);
    }
    lua_pop(L, 1);
    const ofeliaIO &io = dataPtr->io;
    if (io.hasMultiControlOutlets)
    {
        int last = (io.numOutlets >= ac ? ac : io.numOutlets) - 1;
        for (int i = last; i >= 0; --i)
        {
            if (av[i].a_type == A_FLOAT)
                outlet_float(io.outlets[i], av[i].a_w.w_float);
            else if (av[i].a_type == A_SYMBOL)
                outlet_symbol(io.outlets[i], av[i].a_w.w_symbol);
            else if (av[i].a_type == A_POINTER)
            {
                userDataRefVec.push_back(luaL_ref(L, LUA_REGISTRYINDEX));
                outlet_pointer(io.outlets[i], reinterpret_cast<t_gpointer *>(&userDataRefVec.back()));
                luaL_unref(L, LUA_REGISTRYINDEX, userDataRefVec.back());
                userDataRefVec.pop_back();
            }
        }
    }
    else
        outlet_list(dataPtr->ob.ob_outlet, &s_list, ac, av);
    freebytes(av, sizeof(t_atom) * ac);
}

void ofLua::outletRet()
{
    const ofeliaIO &io = dataPtr->io;
    if (!io.hasControlOutlet) return;
    if (lua_isnil(L, -1))
        outlet_bang(io.outlets[0]);
    else if (lua_isboolean(L, -1))
        outlet_float(io.outlets[0], static_cast<t_float>(lua_toboolean(L, -1)));
    else if (lua_isnumber(L, -1))
        outlet_float(io.outlets[0], static_cast<t_float>(lua_tonumber(L, -1)));
    else if (lua_isstring(L, -1))
        outlet_symbol(io.outlets[0], gensym(lua_tostring(L, -1)));
    else if (lua_isuserdata(L, -1))
    {
        userDataRefVec.push_back(luaL_ref(L, LUA_REGISTRYINDEX));
        outlet_pointer(io.outlets[0], reinterpret_cast<t_gpointer *>(&userDataRefVec.back()));
        luaL_unref(L, LUA_REGISTRYINDEX, userDataRefVec.back());
        userDataRefVec.pop_back();
    }
    else if (lua_istable(L, -1))
        outletTable();
}
Zack Lee
  • 2,784
  • 6
  • 35
  • 77
  • `outlet_pointer(io.outlets[0], reinterpret_cast(&userDataRefVec.back()));`What is this? – IS4 Sep 16 '18 at 23:15
  • @IllidanS4 That passes the address of `userDataRefVec`'s last element integer to other Lua chunk so the userData can be used there via `lua_rawgeti()`. I'm sorry I cannot explain more details. – Zack Lee Sep 17 '18 at 00:01
  • Any reason to pass it as a pointer? Why not simply pass the integer? – IS4 Sep 17 '18 at 09:18
  • @IllidanS4 It's because I wanted to distinguish between `number` and `userdata` so they don't clash. – Zack Lee Sep 17 '18 at 09:46

1 Answers1

2

I'm trying to convert userdata to a pointer

Don't. It's not the same, the pointer does not represent the userdata. You can't retrieve Lua userdata object via pointer to its data area.

If you need to manipulate with userdata on native side, save the userdata in registry with luaL_ref(), and use returned integer as a reference. Later you can retrieve that object from registry and get pointer to its data area whenever you need. But don't store just the pointer.

As for the issue with referencing table, the object to be saved in registry with luaL_ref() must be on Lua stack top. But right now the only thing that stays on Lua stack top is the table. You copy that table's value to iterate with while(lua_next()), and anything that's read from the table within that loop is popped from the stack at the end of the loop.

Probably you shouldn't even build the av structure at all, and process the data immediately as you scan the original table. You don't need to ref/unref userdata then. If you really must have that av, then instead of the pointer to userdata's memory save the integer index returned by luaL_ref(). And you should do it in the first loop, not in the second. The second loop then would do lua_rawgeti() to get userdata object, and after processing you can unref it.

Vlad
  • 5,450
  • 1
  • 12
  • 19
  • Thanks for your answer but when I call `luaL_ref(L, LUA_REGISTRYINDEX);` in the first loop that is `while (lua_next(L, -2))`, my app crashes. – Zack Lee Sep 16 '18 at 22:21
  • Moving `lua_pop(L,1);` inside while loop into each conditions except for `else if (lua_isuserdata(L,-1))` made it not crash. Maybe I should not call `lua_pop(L,1)` after calling `LuaL_ref()`? – Zack Lee Sep 16 '18 at 22:28
  • 1
    @ZackLee, saving something with `luaL_ref()` will pop it off the stack, so make a copy with `lua_pushvalue()`. But check this first - if the table you scan will live for entire time of processing, you don't need to save userdata in the registry. You can take the pointer to userdata memory area for `av`, and rely on the fact that the userdata will be kept alive by the table where it's stored. No need to ref/unref it then. – Vlad Sep 17 '18 at 06:16
  • Thank you so much for your help. I will give you a bounty tomorrow. – Zack Lee Sep 17 '18 at 10:29