lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



On 4-Oct-06, at 3:02 PM, Jerome Vuarand wrote:

While trying to answer some recent mails on the list I did some
experiment with environments and noticed that it is not possible to set
anything else than a table as a function environment.

I think it would be nice to be able to set any value as the environment,
especially a userdata. The value in question should have __index and
__newindex metamethods. If it don't variable reads would return nil and
variable writes would be ignored.

An example of use would be a configuration mechanism similar to one that
has been discussed recently. Here a C++/Lua hypothetical example:

Why not just use an empty table whose __index and __newindex methods use the userdata? In effect, they could be the same __index and __newindex metamethods you would use with the userdata.

The following hardly changes your example at all:

(Note: I'm using lightuserdata here, something I would normally never do, but you don't seem to be attaching a __gc metamethod to your userdata so I'm assuming that you are prepared to manage them as resources without assistance from the Lua garbage collector).

So: here we create the proxy environment table and its metatable,
attaching the configuration object as an upvalue to both getter and
setter:

static int pushobject(lua_State* L, Object* object)
{
    // Put object address as a userdata
    lua_pushlightuserdata(L, (void *)object*);
    // Create the environment table
    lua_newtable(L);
    // Create the metatable
    lua_newtable(L);
    lua_pushvalue(L, -3); /* Copy of l.u.d */
    lua_pushcclosure(L, config_setvar, 1);
    lua_setfield(L, -2, "__newindex");
    lua_pushvalue(L, -3); /* Copy of l.u.d */
    lua_pushcclosure(L, config_getchild, 1);
    lua_setfield(L, -2, "__index");
    lua_setmetatable(L, -2);
    return 1;
}

Now, a slight modification to the getter and setter is all it takes:

// Get a child, will be called as __index
static int config_getchild(lua_State* L)
{
    // Small change to next line ********
    Object** pconfig = lua_touserdata(L, lua_upvalueindex(1));
    Object* child = (*pconfig)->getchild(lua_tostring(L, 2));
    if (child)
        return pushobject(L, child);
    else
        return 0;
}

The same change needs to be made to the first line of config_setvar.