1

I have researched this subject and tried various approaches but I can't implement the behavior I have in mind (I'm not even sure it's possible). Basically, I have several userdata objects created in C that can be accessed by their metatable, like this:

Main.lua

config.display_width = 1280

What I'd like to do is to "force" the config namespace to a specific script. You've guessed it, I need to protect a configuration file so that users are restricted to deal only with the config metatable. Like this:

Config.lua

display_width = 1280

And I know I have to do something like this in C:

// Register the config metatable and its methods
luaL_loadfile(L, "my_config.cfg");
lua_getglobal(L, "config"); // Is this necessary?
lua_setfenv(L, -2); // I know this has to be used, but how?
lua_pcall(L, 0, 0, 0);

Thank you in advance, this one is driving me crazy!

PS: For the record, I really need to keep the config userdata as it is because it's binded to a C structure. In consequence, I'm not concerned about "losing" the Lua state or declared variables between different environments.

Adding the following information. This is how the config userdata is being created:

const struct luaL_Reg metaconfig[] =
{
    {"__index", l_get},
    {"__newindex", l_set},
    {NULL, NULL}
};

lua_newuserdata(L, sizeof(void *));

luaL_newmetatable(L, "metaconfig");
luaL_register(L, NULL, metaconfig);
lua_setmetatable(L, -2);

lua_setglobal(L, "config");

So every time the user sets or gets values from the config userdata I update the C structure via the __index or__newindex methods.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294

1 Answers1

1

you don't really need a global representing the config table, you can do with a lua_ref too.

Here this works as expected (I guess):

#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>

int main (void){
    int idxConfig, res;
    lua_State *L = luaL_newstate();
    if ((res = luaL_loadfile(L,"my_config.cfg")) != 0){//Load file
        printf("Got error code %d loading file my_config.cfg, exiting",res);
        exit(-1);
    }
    lua_newtable(L); // new config table
    lua_pushvalue(L,-1);// duplicate table
    idxConfig = lua_ref(L,LUA_REGISTRYINDEX); // take a reference to the table (pops it)
    lua_setfenv(L,-2); // pop table, set as environment for loaded chunk
    lua_call(L,0,0); // load config -- nothing on stack
    lua_rawgeti(L,LUA_REGISTRYINDEX,idxConfig); //push config table
    lua_getfield(L,1,"display"); //read out "display"
    lua_Integer disp_width = lua_tointeger(L,-1);
    printf("Display width = %d",(int) disp_width);
    lua_close(L);
    exit(0);
}
jpjacobs
  • 9,359
  • 36
  • 45
  • I'm sorry, I'm afraid that's not quite it. Remember that I already *have* the config userdata created. So every time the user sets or gets a config field in reality I'm exchanging data between my own C structure. – Agustín Cordes Feb 15 '11 at 19:57
  • Actually, I can use your solution but I'm forced to traverse the resulting table from the call to my_config.cfg. That's not quite what I had in mind (as I explained in my edit), but I guess it can be done if there are no better proposals. Thanks! – Agustín Cordes Feb 15 '11 at 21:00
  • But wait, the config thing is a simple table, no? Because you keep calling it a "userdata". In my example I just use a fresh table, you would get your config table from the registry or from the globals table of the lua_State you're using, no? – jpjacobs Feb 16 '11 at 09:00
  • No, that's the thing; see the new code I posted above. Config is a userdata with metatable and all. So every time the user accesses config the __index and __newindex are triggered in C. – Agustín Cordes Feb 16 '11 at 12:22
  • AFAIK a userdatum can not be set as the environment in Lua. The Reference always says 'table'. So, I guess the question is: do you really want to use this userdatum directly or would it be easier to just `setfenv` a table for the chunk, and read out its contents after calling it? – jpjacobs Feb 16 '11 at 12:44
  • Exactly, that's why your answer gave me some food for thought. I too think now that it's simply not possible to use a userdatum as the environment... I was hoping for a shortcut, but no. In any case, I'm voting this up! Thanks! – Agustín Cordes Feb 16 '11 at 14:12