lua-users home
lua-l archive

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


On Sat, 24 Mar 2012 14:34:46 -0700 (PDT)
codemonkey Monkey <thecodemonkey@rocketmail.com> wrote:

> 
> 
> 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!

Hi Codemonkey,

It sounds to me like what you really want is the Lua equivalent of the
(Linux/Unix) bash export statement, which makes changes made to
environment variables in this program available in all programs
forked/execed from this program. And it sounds like according to your
research, there's no such thing in Lua.

If there's no such thing, what's wrong with every subroutine making a
call to a quick program to retrieve the intended environment? You say
above that you don't want to do that, but it doesn't sound that bad to
me. In the following discussion, I'll use generalized OOP terms, as
there are lots of ways to do OOP in Lua...

You could make a class that implements a stack of environments, with
each environment being a table of envvar/value pairs. You'd also have a
persistent object variable that keeps track of your current level in
the stack. Such a class would need the following methods:

* getenvtable() gets the environment table at the lowest level, and then
  sets the level counter one deeper. This is run at the beginning of
  every function.
* setenv(envvar, value) sets the current level's envvar to whatever
  value, and just for fun returns that value.
* return() tells the stack object you're done with this function, and
  to decrement the stack level. This is called at the end of every
  function that has called getenvtable(), just before the function's
  return statement.

So a function might look something like this:

function whatever(stackobj)
	envtable = stackobj:getenvtable()
	-- Do some stuff
	envtable[minutes] = stackobj:setenv(minutes+1)
	-- Do more stuff, then call another function
	son_of_whatever(stackobj)
	envtable[color] = stackobj:setenv(green)
	-- Do more stuff
	stackobj:return()
end

I'm not sure why simply passing envtable every time wouldn't work, but
if it didn't, I imagine this stackobj technique would.

HTH

SteveT
	

I haven't implemented this exact stack, but it seems to me like it
wouldn't be difficult, and I doubt it would be especially slow.