[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: multiple lua files with same-named functions
- From: Javier Guerra <javier@...>
- Date: Sat, 6 May 2006 09:22:32 -0500
On Friday 05 May 2006 10:47 pm, Raymond Jacobs wrote:
> I am trying to do all of this from C,
> so that example doesn't really help me,
> I especially don't know what __index is =/
>
> as I understand it, i want to set my environment to a, 'temporary' one,
> load the code, and then set it back (so i get all of my nice global
> variables and registred C functions and such)
> is that right? and if so how would I acomplish that in C?
in fact, it's easiest to do all from Lua, if you REALLY need it from C, you
could do a prototype in Lua and then port it to C.
i'll try to explain most of the concepts here, first in Lua, and then try to
show how to port it to C.
first, when you load a file, what you get is a function representing the whole
chunk. assume that the file defines some functions, like this:
----- script.lua --------
function foo (x)
return x * 2
end
function bar (t)
return foo(t.x)
end
--------------------
this Lua chunk in fact is two separate assignment statements:
----- script.lua -------
foo = function (x)
return x*2
end
bar = function (t)
return foo (t.x)
end
---------------------
to get the functions 'foo' and 'bar' defined, you have to execute the chunk
you get when loading the file:
--- script loader (in lua) ----
local chunk = loadfile ("script.lua") -- loads/compiles the lua chunk
chunk() -- executes the chunk
foo(2) -- calls a script function
------------------------------
if you do that, the script would set two global functions (foo and bar) in the
global environment. that's what you want to avoid. to do that, just change
the environment for the chunk function, like this:
--- script loader (in lua) ----
local scriptenv = {} -- a new table (one for each script)
local chunk = loadfile ("script.lua") -- loads/compiles the lua chunk
setfenv (chunk, scriptenv) -- sets the 'global' env for the script
chunk() -- executes the chunk
scriptenv.foo(2) -- calls a script function
-----------------------------
now, the foo and bar variables won't be set in the 'real' global environment,
but in a new 'private' global space. you can use a new one for each script,
or maybe if you have several groups you could use a different environment for
each group. scripts within one group would 'see' the same environment and
could share global variables or functions.
but..... the localized environment is empty, the scripts can't access any
function then don't define. not even 'standard' ones, like print(), or
open(), or any utility function you've defined for use.
the 'easy' solution is to make the private environment 'inherit' from the real
global environment. enter metatables
a metatable is a table associated with another object. it defines some
properties, like how to fetch and set for extra fields. specifically, we
need here the '__index' property. if you set the '__index' field of a
metatable, then it'll be used when trying to fetch a nonexistant key from the
object that uses this metatable. __index can contain either a function to be
called to fetch the key, or (easier to use) a table to use as a 'fallback'.
to make the private environment inherit from the global one:
--- script loader (in lua) ----
local scriptenv = {} -- a new table (one for each script)
local scriptenv_mt = {__index=_G} -- the metatable for scriptenv
setmetatable (scriptenv, scriptenv_mt)
local chunk = loadfile ("script.lua") -- loads/compiles the lua chunk
setfenv (chunk, scriptenv) -- sets the 'global' env for the script
chunk() -- executes the chunk
scriptenv.foo(2) -- calls a script function
-----------------------------
now, when some code in the script tries to fetch a 'global' function (like
print()), it won't find it in the environment, so Lua would look in the
metatable, follow a link to the 'real' environment, and find it there.
this way, you can expose the whole 'global' environment, but at the same time
protect it from being modified. any 'global' changes the script does are
limited to its private environment.
you can refine this a little if you want to define a small subset of
functionality. for example, you might not want the scripts being able to
open files, or you want them to use a special version of print(), that sends
data to your system, instead of going to the screen:
---------------------------------
local cage = {
-- import some packages:
string = string, coroutine = coroutine,
table = table, math = math,
-- some 'global' functions:
next = next, ipairs = ipairs, pairs = pairs,
require = require, type = type,
tonumber = tonumber, tostring = tostring,
unpack = unpack,
setmetatable = setmetatable,
getmetatable = getmetatable,
-- modified global functions:
print = myprint,
error = myerror
-- my own api:
move = move
kill = kill
}
local mt = {__index=cage}
function scriptloader (scriptname)
local scriptenv = {}
setmetatable (scriptenv, mt)
local chunk = loadfile (scriptname)
setfenv (chunk, scriptenv)
chunk ()
return scriptenv
end
---------------------------
here you define a specific subset of Lua functionality, to be available to
your scripts. then, each script has it's own environment, that 'inherits'
from this "cage". the scriptloader() function loads a script file and
executes it into this new private environment, and returns it. if the script
defines 'global' functions, they will be registered only in this new
environment, without 'polluting' the real global environment.
you can have a table with the new environments, like this:
-------------
allscripts = {}
allscripts[1] = scriptloader ("script.lua")
allscripts[2] = scriptloader ("secondscript.lua")
script[1].foo(x) -- call the loaded function from first script
script[2].foo(x) -- from second script
--------------
now......... you want to do this from C? the easiest way is to just call
scriptloader()
--
Javier
Attachment:
pgpqIuFTMMz8S.pgp
Description: PGP signature