lua-users home
lua-l archive

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


于 2012-3-25 5:34, codemonkey Monkey 写道:

I"m using LUA to extend a game engine.  It's generally working OK (could wish for static type checking though...), but I've run into something I can't work out, and searching the list archives hasn't turned up an answer for any search I've tried so far.

The behavior I'm after seems like it should be very simple, maybe the most common thing to want, but I can't find a combination of metatable/setfenv behaviors to do it.  I have a bunch of C++ internal game objects.  Each of these C++ objects has a Lua table associated with it which defines a desired Lua global environment.  All I want is to call a Lua fn from C++, and have this environment table be used for the Lua fn I call, and anything that *it* calls, recursively for the entire call stack.  In other words, I want my environment to be inherited.

I started out by trying to use setfenv on the Lua fn from C++ before I call it, pointing to my environment.  For example, let's call this lua function LuaTest.  I quickly discovered that the setfenv is not inherited through the call stack!  I had to call setfenv in *every* Lua function called in the entire tree that LuaTest might call, which worked, but was not practical at all.

My second atttempt, and the one I have limping along now, was to define a metatable with __index and __newindex entries pointing to my desired environment for LuaTest, and setting this as the metatable for the module that contains LuaTest (*).  This *almost* works.  Every Lua fn called by LuaTest sees my environment table exactly as I wish but *only* within the module defining LuaTest.   Once any function is called outside that module, my environment is lost, replaced with the one defined by the setfenv of the called Lua function. 

That's where I'm at now: I have the behavior I want but only within a single module.  What I really wish is something that seems so very simple: I want the environment i set to be inherited by anything called by LuaTest.  But I can't seem to get that behavior!  I've tried some of the "pseudo-index" things too, but whatever I try, it seems some "function environment" always clobbers the real environment I'm trying to have inherited down through the Lua call stack.

Is there a nice simple way to do this?

For a while I was just setting a global "E" with my desired environment, and everybody had to say "E.myvar ...".  That also works, even across modules, but it's a pain, and fragile: if anyone forgets to say "E.myvar", even just once, there's a bug.  With the metatable approach, at least I *know* variables are going into the environment I want them to.

Thanks!


(*) Actually I'm not using the Lua module system, but my own, but that's not related to my query above: I see the same troubles when using Lua's module system.  Mine provides more private module behavior than the Lua module system, so you can say:

local my_module = Game.require('some/file.lua')

Your handle to it is private, and you can say my_module.some_function().  But I want some_function() to see the same Lua environment that its caller does!


IMHO, Lua is **lexical** scoped, not dynamic scoped, and what are callable are actually closures, not function prototypes.
a closure is an object(or instance) of one prototype, with some special variables, i.e. the upvalues, linked with it.
and the upvalues get linked to the closures when the closure is created, not when the closure is called.
what's more, I see the **environment** of a closure as a special upvalue(and in Lua 5.2, it is a upvalue), which is also attached to a closure when it is created. (a function prototype has no environments)

so you may not easily change the environment of closures when they got called.

but I do have a means to simulate this behavior, but with huge performance degradation.
if you don't care about the performance degradation, you may try this:

1.create a empty table _EMPTY(with its metatable _MT) as environment to load/compile all the scripts.
       you may need to set the _MT.__index and _MT.__newindex to ensure the compilation would suceed.

2. before calling some closure/function, if you want use _ENV as the environment,
       just set the _MT.__index and __MT.__newindex to point to _ENV.



BTW:
although this might work, I strongly recommend you rethink you design in Lua.
USE LUA IN THE WAY LUA IS.