[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Interfacing lua with C
- From: RLake@...
- Date: Thu, 16 Jan 2003 12:52:17 -0500
> Regarding your code:I get a "attempt to inde 'old_meta' (a nil value)
> on the fourth line.
>
> > do
> > local meta, getters, setters = {}, {}, {}
> > local old_meta = getmetatable(getglobals())
> > local old_index, old_newindex = old_meta.__index, old_meta.__newindex
> >
> > -- populate getters and setters somehow, probably by getting them
>[...]
Quite right, my bad. I realised that while walking home last night. It is
quite likely that the existing globals table does not have a metatable. I
was trying to be too clever. The correction is below.
> But still...can I get around the newindex problem to be able to assign
> values to my C vars more than once?If not all else is useless.
Sure. Your C vars never show up in the globals table, so __newindex and
__index are triggered every time. The point of the code I wrote was to only
apply functions to your personal variables. The idea is that if the key
doesn't exist (i.e. the global has never been defined), then your getter or
setter function is called instead of putting the key into the globals
table. It knows which are your variables because you have populated the
getter and setter tables with the corresponding keys.
If the variable does exist, then __index and __newindex will not be called,
so you have to insert the metamethod before anyone has a chance to shadow
your variables. (Or you could set them to nil yourself, of course.)
If the variable does not exist and it is not yours, then the default is to
use the existing metatable, if any, (which the code I wrote handles
correctly), to return nil on a get, and to set the variable on a set. I
made a mistake on the last one. (Oops!)
So here is the corrected code and some sample output.
---- CUT HERE ---
-- Version 0.2
-- Fixed the case where the globals table didn't have a metatable
-- Corrected the behaviour on set where there was no existing
-- __newindex metamethod so that it now rawsets the table
-- Check to see if the old __index method is not a function
-- to mimic the default behaviour
-- Wrote a couple of quick example getters and setters
-- Actually made sure it compiles and runs
--
-- TODO
-- Actually debug it with real metatables
-- Think of a setter that lets you set something
do
local meta, getters, setters = {}, {}, {}
local old_meta = getmetatable(getglobals())
local old_index, old_newindex
if old_meta then
old_index, old_newindex = old_meta.__index, old_meta.__newindex
end
-- populate getters and setters somehow, probably by getting them
-- from your C code. Here is an example:
local function get_time(k)
if k == "gmt"
then return os.date("!%c")
else return os.date("%c")
end
end
local function set_time(k)
error "You cannot change the time"
end
getters.date = get_time
getters.gmt = get_time
setters.date = set_time
setters.gmt = set_time
local function get_env(k)
return os.getenv(k)
end
local function set_env(k, v)
if os.setenv
then
os.setenv(k, v)
else
error "You cannot change environment variables on this platform."
end
end
getters.USER = get_env
setters.USER = set_env -- hmm? it's just an example
-- you might want to change the calls below to object calls,
-- such as getters[k](getters[k], k)
-- For efficiency, you probably only want to do that lookup once.
meta = {}
if type(old_index) == "function" then
function meta.__index(t, k)
if getters[k]
then return getters[k](k)
else return old_index(t, k)
end
end
elseif type(old_index) == "nil" then
function meta.__index(t, k)
if getters[k] then return getters[k](k) end
end
else
function meta.__index(t, k)
if getters[k]
then return getters[k](k)
else return old_index[k]
end
end
end
if old_newindex
then
function meta.__newindex(t, k, v)
if setters[k]
then setters[k](k, v)
else old_newindex(t, k, v)
end
end
else
function meta.__newindex(t, k, v)
if setters[k]
then
setters[k](k, v)
else
rawset(t, k, v)
end
end
end
setmetatable(getglobals(), meta)
end
---- CUT HERE ---
-- date is now proxied by a C function
Sample output:
> print(date)
Thu Jan 16 12:34:40 2003
-- so is gmt
> print(gmt)
Thu Jan 16 17:35:34 2003
-- setting "works"; the variable is read-only
> date = "tomorrow"
glue.lua:27: You cannot change the time
stack traceback:
[C]: in function `error'
glue.lua:27: in function `?'
glue.lua:91: in function <glue.lua:88>
stdin:1: in main chunk
[C]:[C]
-- This mechanism might be useful in a CGI script, for example
> print(USER)
rlake
-- Most platforms implement setenv but it's not ANSI standard. This
-- would work if you patched the os library.
> USER="root"
glue.lua:44: You cannot change environment variables on this platform.
stack traceback:
[C]: in function `error'
glue.lua:44: in function `?'
glue.lua:91: in function <glue.lua:88>
stdin:1: in main chunk
[C]:[C]
-- Ordinary globals continue to be ordinary.
> print(j)
nil
> j = 7
> print(j)
7