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: pgpuHMSk5H6nB.pgp
Description: PGP signature