lua-users home
lua-l archive

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


> 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