lua-users home
lua-l archive

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


Wesley Smith wrote:
> Do you have an URL of this?
> http://lua-users.org/wiki/UserdataEnvironmentExamples turns up an
> empty page. 
> 
> wes

Here is a freshly written complete and tested example, feel free to put
it on the wiki:

/*** module.c *****************************************************/
#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>

#define FOO "foo"

static int lua__foo__do_it(lua_State* L)
{
    printf("it's done in C\n");
    return 0;
}

luaL_Reg foo_methods[] = {
    {"do_it", lua__foo__do_it},
    {0, 0},
};

static int lua__new(lua_State* L)
{
    /* Create a new userdata */
    lua_newuserdata(L, sizeof(int));
    /* Set common metatable to that userdata */
    lua_getfield(L, LUA_REGISTRYINDEX, FOO);
    lua_setmetatable(L, -2);
    /* Set a specific environment table to that userdata */
    lua_newtable(L);
    lua_setfenv(L, -2);
    return 1;
}

luaL_Reg module_functions[] = {
    {"new", lua__new},
    {0, 0},
};

/* __index(self/U, key) */
static int lua__foo___index(lua_State* L)
{
    int U = *(int*)lua_touserdata(L, 1);
    if (lua_isnil(L, 2))
    {
        luaL_typerror(L, 2, "non-nil key");
    }
    /* Retrieve environment variable */
    lua_getfenv(L, 1);
    /* Check if that key points to something in environment */
    lua_pushvalue(L, 2);
    lua_gettable(L, -2);
    if (!lua_isnil(L, -1))
    {
        return 1;
    }
    /* Pop environment and nil */
    lua_pop(L, 2);
    /* Fallback: Retrieve methods in metatable */
    lua_getmetatable(L, 1);
    lua_getfield(L, -1, "methods");
    /* Look for key in methods */
    lua_pushvalue(L, 2);
    lua_gettable(L, -2);
    /* Simply return it, even if its nil */
    return 1;
}

static int lua__foo___newindex(lua_State* L)
{
    int U = *(int*)lua_touserdata(L, 1);
    if (lua_isnil(L, 2))
    {
        luaL_typerror(L, 2, "non-nil key");
    }
    /* Make sure we have a value in slot 3 */
    if (lua_gettop(L)==2)
        lua_pushnil(L);
    /* Retrieve environment variable */
    lua_getfenv(L, 1);
    /* Set the value in the environment table */
    lua_pushvalue(L, 2); /* Push key */
    lua_pushvalue(L, 3); /* Push value */
    lua_settable(L, -3); /* Assign */
    return 0;
}

int luaopen_module(lua_State* L)
{
    /* Create the metatable */
    luaL_newmetatable(L, FOO);
    /* Put userdata methods in metatable.methods */
    lua_newtable(L);
    luaL_register(L, 0, foo_methods);
    lua_setfield(L, -2, "methods");
    /* Set a __index metamethods that accesses environment table and
     * fallback to metatable.methods */
    lua_pushcfunction(L, lua__foo___index);
    lua_setfield(L, -2, "__index");
    /* Set a __newindex metamethods that put everything in the
     * environment */
    lua_pushcfunction(L, lua__foo___newindex);
    lua_setfield(L, -2, "__newindex");
    
    /* Create the module */
    lua_newtable(L);
    luaL_register(L, 0, module_functions);
    return 1;
}
/******************************************************************/

---- test.lua ------------------------------------------------------
local module = require 'module'

local U = module.new()

U:do_it()

U.do_it = function() print("it's done in Lua") end

U:do_it()
--------------------------------------------------------------------

It should output:
it's done in C
it's done in Lua

Feel free to ask for more comments.