[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 16:53:19 -0500
> But I was hoping on getting away with
> using full userdata to store structs with all info on a
> cvar(pointer,flags etc.) and keep those in the global table w/o having
> to use multiple tables.
You should probably model C structures with Lua tables (or the
as-yet-undocumented proxies, which look to be quite nice). Then the problem
is much simpler because you can define metamethods for the proxy
tables/proxies without worrying about the global namespace at all.
However, scalar C proxies still strike me as an interesting question. So
here is another solution.
To pull this off, you need to at least associate a known metatable with
each different type of C variable, but I suppose you would do that anyway.
Let's suppose that the metatable has at least the keys __get and __set
whose values are (C) functions to get and set the values of the C scalars.
You need some way to create a C variable
which will create an object with the metatable. Once that is assigned to a
global, future sets and gets to that
global are deferred to the __get and __set metamethods so you can never get
rid of the binding (well, there could
be an unbind function but I didn't write that.)
These metatables pass for the "type" of the C value, and I will assume that
we have several of these. This time I'm not going to deal with the
possibility that the globals table has its own metamethods, although the
same strategy would work for that case. It just makes the code harder to
follow and it is hard enough to follow as it is :)
For efficiency I'm going to assume that you are not going to want to
redefine existing globals as C-values and that there will not be a lot of
new non C-value globals after this procedure is executed. That means that
system library functions will still be looked up without interference.
----- CUT HERE
do
local types = {} -- table of metatables we're going to deal with
local global_meta = {} -- metatable for globals
local sub_meta = {} -- metatable for new globals
local c_proxied = {} -- table of proxied values.
local new_globals = {} -- normal globals
-- use this function to register your metatables (types)
function register_type(meta)
types[meta] = true
end
-- We know the key doesn't exist in the globals table,
-- which contains pre-existing globals. It
-- might be new global, or it might be a proxied c-value.
-- We defer the lookup to the new_globals
-- table which has its own __index metamethod
function global_meta.__index(t, k)
return new_globals[k]
end
-- This gets invoked if the key wasn't found in the
-- new_globals table. So it is either a
-- proxied c-value or an undefined global
function sub_meta.__index(t, k)
local val = c_proxied[k]
if val then return getmetatable(val).__get(val) end
end
-- With set, we need to follow a different strategy, because we need to
-- intercept every attempt to set a global (this doesn't happen
-- with pre-existing globals).
function global_meta.__newindex(t, k, v)
-- first we see if we're proxying this key, and if so we just pass on
-- the value. c_proxied does not have metamethods, so we don't need
rawget.
local cval = c_proxied[k]
if cval then
getmetatable(cval).__set(cval, v)
else
-- get the *value*'s type (well, metatable)
-- and see if it is one of ours.
local meta = getmetatable(v)
if meta and types[meta] then
-- It is a c_val, so we need to get rid of it from regular
globals,
-- in case it exists, and put it into the proxied table
new_globals[k] = nil
c_proxied[k] = v
else
-- it is neither an existing c_value nor a new one. So we just
-- stash it away
new_globals[k] = v
end
end
end
setmetatable(getglobals(), global_meta)
setmetatable(new_globals, sub_meta)
end
-- Quick test
do
local date_meta = {}
register_type(date_meta)
-- getter returns the date according to the object's format
function date_meta.__get(date_obj)
return os.date(date_obj.fmt)
end
-- setter changes the object's format
function date_meta.__set(date_obj, fmt)
date_obj.fmt = fmt
end
-- constructor
function Date(fmt)
return setmetatable({fmt = fmt or "%c"}, date_meta)
end
end
----- CUT HERE
-- sample output
> now = Date()
> print(now)
Thu Jan 16 16:41:13 2003
> gmt = Date("!%c")
> print(gmt)
Thu Jan 16 21:41:22 2003
> gmt = "!%F %R"
> print(gmt)
2003-01-16 21:41
>