4

This is probably an easy question, but I am stumped. This is for Lua 5.1.

I have a script which runs in its own environment. In that environment, I have a variable called "plugin" I set from C++ like so:

    lua_getfield(L, LUA_REGISTRYINDEX, getScriptId());  // Put script's env table onto the stack  -- env_table

    lua_pushstring(L, "plugin");  //                           -- env_table, "plugin"
    luaW_push(L, this);       //                               -- env_table, "plugin", *this
    lua_rawset(L, -3);        // env_table["plugin"] = *this   -- env_table

    lua_pop(L, -1);           // Cleanup                       -- <<empty stack>>

Before running my Lua script, I set the function environment like so:

 lua_getfield(L, LUA_REGISTRYINDEX, getScriptId());    // Push REGISTRY[scriptId] onto stack           -- function, table
 lua_setfenv(L, -2);                                   // Set that table to be the env for function    -- function

When my script runs, it can see and interact with the plugin variable, as expected. So far, so good.

At one point, the Lua script calls a C++ function, and in that function, I want to see if the plugin variable is set.

I have tried many many things, and I can't seem to see the plugin variable. Here are but 4 things I tried:

lua_getfield(L, LUA_ENVIRONINDEX, "plugin");
bool isPlugin = !lua_isnil(L, -1);
lua_pop(L, 1);    // Remove the value we just added from the stack

lua_getfield(L, LUA_GLOBALSINDEX, "plugin");
bool isPlugin2 = !lua_isnil(L, -1);
lua_pop(L, 1);    // Remove the value we just added from the stack

lua_getglobal(L, "plugin");
bool isPlugin3 = !lua_isnil(L, -1);
lua_pop(L, 1);    // Remove the value we just added from the stack

lua_pushstring(L, "plugin");
bool isPlugin4 = lua_isuserdata(L, -1);
lua_pop(L, 1);

Unfortunately, all the isPlugin variables return false. It is as if the C++ function called from Lua cannot see a variable set in a Lua environment.

Any idea how I can see the plugin variable from C++?

Thanks!

3
  • What is luaW_push? That's not standard Lua code. Commented Nov 8, 2012 at 3:55
  • We're using a modified version of LuaWrapper (bitbucket.org/alexames/luawrapper/src) to help with our binding, and that's a function for pushing userdata onto the stack. I tried to remove most of the custom stuff, but missed that one. Commented Nov 8, 2012 at 7:32
  • Are you pushing the string "plugin" back to the stack when calling the c++ function from the Lua script? Commented Nov 8, 2012 at 23:43

2 Answers 2

2

Every function in Lua has it's own environment. They don't inherit the environment of whomever calls them. So if your C++ function doesn't use the environment that has this plugin variable, then it won't see it.

Sign up to request clarification or add additional context in comments.

2 Comments

Is there any way the C++ function called from Lua can access the caller's environment?
Or, better, is there a way to tell the function to use that environment? That's probably what I really want.
0

You could pass the environment to the C function as part of the closure (see lua_pushcclosure). I don't know the kind of setup you have, but I can see three ways this can pan out:

1) Your C function is registered in the same environment as the function - good, will work.
2) Your C function is registered in the global environment but the Lua functions which will call it all reside in one specific environment - will still work if the environment exists when the function is registered (so it can be added to the closure).
3) Your C function is registered in the global environment and can be called by different Lua functions working in different environments - will not work anymore.

If it's 2 or 3, there might be no drawbacks if you change the implementation to use variant 1.

Edit: Alright, so that won't work. There is a way to obtain under-the-hood information if you are willing to stray a bit from the Lua API. DISCLAIMER: I am working with 5.2, so I am trying to adapt my methods for 5.1. I could not test this, and it might not work.

First you will need to #include "lstate.h"

This is the lua_State structure in 5.1:

struct lua_State {
  CommonHeader;
  lu_byte status;
  StkId top;  /* first free slot in the stack */
  StkId base;  /* base of current function */
  global_State *l_G;
  CallInfo *ci;  /* call info for current function */
  const Instruction *savedpc;  /* `savedpc' of current function */
  StkId stack_last;  /* last free slot in the stack */
  StkId stack;  /* stack base */
  CallInfo *end_ci;  /* points after end of ci array*/
  CallInfo *base_ci;  /* array of CallInfo's */
  int stacksize;
  int size_ci;  /* size of array `base_ci' */
  unsigned short nCcalls;  /* number of nested C calls */
  lu_byte hookmask;
  lu_byte allowhook;
  int basehookcount;
  int hookcount;
  lua_Hook hook;
  TValue l_gt;  /* table of globals */
  TValue env;  /* temporary place for environments */
  GCObject *openupval;  /* list of open upvalues in this stack */
  GCObject *gclist;
  struct lua_longjmp *errorJmp;  /* current error recover point */
  ptrdiff_t errfunc;  /* current error handling function (stack index) */
};

Let's assume L is your lua_State*. As you can see, L->ci holds the current callinfo, and the callinfo array is contained between L->base_ci and L->end_ci.

So the Lua function which called your C function is situated at (L->end_ci-2) (which should be the same as (L->ci-1)), and its stack id (StkId) is (L->end_ci-2)->func. We can trick the Lua API into letting you work with stack ids which are below the current calling function by doing something like this:

StkId saved = L->base;
L->base = L->base_ci->base;
int idx = (L->end_ci-2)->func - L->base+1;
lua_getfenv(L, idx);
L->base = saved;

The environment table should be on top of the stack now.

Edit: The Lua API checks for a valid index are a bit tricky. This should fool them.

2 Comments

An interesting approach, but will not work in my case. A function can be called in one of several contexts (in the editor, as a trigger, etc.) and I am trying to figure out which context it is based on variables in the caller's scope.
You can register multiple C closures with the same function. When you do lua_setfenv on your function, push into its environment a C closure with your function and a single upvalue which is a reference to that environment (a table). Anyway, I've edited my answer to give a hack-ey solution which might be more convenient.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.