简体   繁体   中英

Accessing Lua variable in function environment from C++

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!

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM