I have written some code to separate registering custom functions and the __newindex and __index functions into 2 separate functions. The goal of my code is to have functions and variables visible to the Lua script writer that are organized based upon sublevels of specificity. For example, the user would have available the following commands:
orc.chief.attack();
orc.chief.flee();
orc.chief.hp = 100;
orc.pawn.attack();
elf.wood.attack();
elf.wood.hp = 200;
So basically a system with 2 tiers and then a function call or a variable. If I understand Lua correctly, that is a metatable in a table in a table. When the user sets the variable, it should trip a __newindex call (not only to handle setting the value but to access a physical object that will animate through motors). I also assume that the chief table in the table orc just sees lots of functions assigned to it regardless whether it is attack or __newindex. To make it easier to add new variables and functions as the code develops, I have created 2 functions: one to create a function and one to create a variable. The function create just registers the functions and the variable create just makes a new table element and registers the functions for __newindex and __index. Below is the code:
int orcChiefhp;
luaL_Reg Orc_Module[] = {
{"attack", OrcAttack},
{"flee", OrcFlee},
{NULL, NULL}};
const luaL_Reg orcChief_metareg[] = {
{"__index", orcChief__index},
{"__newindex", orcChief__newindex},
{NULL, NULL}};
int OrcAttack(lua_State *L)
{
//code to cause the motors to swing the weapon...
return 0;//0 parameters come back as the data
}
int orcChief__newindex(lua_State *L)
{
const char *idx;
if(lua_isstring(L,2))
{
idx = lua_tostring(L,2);//gets the string so we can get the variable of the struct
if(strcmp(idx, "hp")==0)
{
lua_pushnumber(L, orcChiefhp);
}
else
lua_pushnil(L);
}
return 1;
}
void registerFunctions(lua_State *L, const char *libname, const char *sublibname, const luaL_Reg *funcs)
{
int isitnil;
lua_getglobal(L, libname);
isitnil = lua_isnil(L, -1);
if(isitnil)
{
lua_pop(L, 1);
lua_newtable(L); // create 'libname' table
}
// no sublib: just import our library functions directly into lib and we're done
if (sublibname == NULL)
{
luaL_setfuncs(L, funcs, 0);
}
// sublib: create a table for it, import functions to it, add to parent lib
else
{
lua_newtable(L);
luaL_setfuncs(L, funcs, 0);
lua_setfield(L, -2, sublibname);
}
if(isitnil)
lua_setglobal(L, libname);//this will pop off the global table.
else
lua_pop(L, 1);//the global table is still on the stack, pop it off
}
void registerIntegerVariable(lua_State *L, const char *libname, const char *sublibname, const char *variableName,
const char *metatableName, const luaL_Reg *metatableFuncs, int defaultValue)
{
int isLibnameNil;
int isSubnameNil;
lua_getglobal(L, libname);//get the libname
isLibnameNil = lua_isnil(L, -1);//check to see if it exists
if(isLibnameNil)//if it doesn't exist, create a new one
{
lua_pop(L, 1);//pop off the nil
lua_newtable(L); // create 'libname' table
}
// no sublib: just import our library functions directly into lib and we're done
if (sublibname == NULL)//if we want the functions at the lib level then just set the functions
{
lua_pushstring(L, variableName);//push the variable name
lua_pushnumber(L, defaultValue);//push the default value on the stack
lua_rawset(L, -3);//add the variable to the table (rawset is like settable but doesn't call __index)
luaL_newmetatable(L, metatableName);//create the metatable
luaL_setfuncs(L, metatableFuncs, 0);//set the metatable functions for __newindex and __index
lua_setmetatable(L, -2);//set the metatable to the libtable
}
// otherwise we need to create a table for the sublibname, import functions to it, add to parent lib.
else
{
lua_getfield(L, -1, sublibname);//see if the sublibname is under the global libname
isSubnameNil = lua_isnil(L, -1);//is it a nil
if(isSubnameNil)//if it is, then we need to create the sublibname
{
lua_pop(L, 1);//pop off the nil
lua_newtable(L);//creates the new sublibname table
}
lua_pushstring(L, variableName);//push the variable name
lua_pushnumber(L, defaultValue);//push the default value on the stack
lua_rawset(L, -3);//add the variable to the table and push it (rawset is like settable but doesn't call __index)
luaL_newmetatable(L, metatableName);//create the metatable
luaL_setfuncs(L, metatableFuncs, 0);//add the metamethods
lua_setmetatable(L, -2);//set the metatable to the sublibname
if(isSubnameNil)
lua_setfield(L, -2, sublibname);//now we need to add the sublibname to the libname
}
if(isLibnameNil)
lua_setglobal(L, libname);//set the global name if it was new
else
lua_pop(L, 1);
}
Then, in my main() I call the functions like this:
execContext = luaL_newstate();
//adding lua basic library
luaL_openlibs(execContext);
//now register all the functions with Lua
registerFunctions(execContext, "orc", "chief", Orc_Module);
registerFunctions(execContext, "orc", "pawn", Orc_Module);
registerFunctions(execContext, "elf", "wood", Elf_Module);
//now register all the variables with Lua
registerIntegerVariable(execContext, "orc", "chief", "hp", "chief_meta", orcChief_metareg, 0);
When I run the code and pump in Lua scripts, orc.chief.attack() calls my OrcAttack() function but orc.chief.hp = 100 never calls my orcChief__newindex() function. I have even commented out the registerFunctions calls in case they were interfering somehow and just the registerIntegerVariable by itself still won't trigger the orcChief__newindex(). Any ideas?
__newindex is not called when you set a field in a table. It is called when you set a new field in a table. If the field already exists, __newindex will not be called.
If you want __newindex to be called for every set operation on a table, you can't allow set operations to actually modify that table. This is generally done by creating an empty table, called a proxy table, which the user uses. The proxy table is actually empty and must always remain so; you intercept all of the get and set calls, piping them to an internal table that the user never sees don't have access to.
Or you use some userdata instead of a table. __newindex is always called for them.
Related
I am trying to wrap ncurses in Lua using the C API. I am working with the stdscr pointer: This is NULL before initscr is called, and initscr is called from Lua by design of my bindings. So in the driver function I do this:
// Driver function
LUALIB_API int luaopen_liblncurses(lua_State* L){
luaL_newlib(L, lncurseslib);
// This will start off as NULL
lua_pushlightuserdata(L, stdscr);
lua_setfield(L, -2, "stdscr");
lua_pushstring(L, VERSION);
lua_setglobal(L, "_LNCURSES_VERSION");
return 1;
}
This works as intended. The trouble comes when I need to modify stdscr. initscr is bound like this:
/*
** Put the terminal in curses mode
*/
static int lncurses_initscr(lua_State* L){
initscr();
return 0;
}
I need to moify the stdscr in the library to no longer be null. Example code from Lua side:
lncurses = require("liblncurses");
lncurses.initscr();
lncurses.keypad(lncurses.stdscr, true);
lncurses.getch();
lncurses.endwin();
But, lncurses.stdscr is NULL, so the it's essentially running the c equivalent of keypad(NULL, true);
My question being, how do I modify library values in Lua after the library is created?
You can use the registry.
Lua provides a registry, a predefined table that can be used by any C code to store whatever Lua values it needs to store. The registry table is always located at pseudo-index LUA_REGISTRYINDEX. Any C library can store data into this table, but it must take care to choose keys that are different from those used by other libraries, to avoid collisions. Typically, you should use as key a string containing your library name, or a light userdata with the address of a C object in your code, or any Lua object created by your code. As with variable names, string keys starting with an underscore followed by uppercase letters are reserved for Lua.
Store a reference to the module table in the registry on creation.
LUALIB_API int luaopen_liblncurses(lua_State* L) {
luaL_newlib(L, lncurseslib);
// This will start off as NULL
lua_pushlightuserdata(L, stdscr);
lua_setfield(L, -2, "stdscr");
lua_pushstring(L, VERSION);
lua_setglobal(L, "_LNCURSES_VERSION");
// Create a reference to the module table in the registry
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "lncurses");
return 1;
}
Then when you initscr, update the field.
static int lncurses_initscr(lua_State* L) {
initscr();
// Update "stdscr" in the module table
lua_getfield(L, LUA_REGISTRYINDEX, "lncurses");
lua_pushlightuserdata(L, stdscr);
lua_setfield(L, -2, "stdscr");
return 0;
}
What is the best way to deal with nested lua_CFunction calls? Assume I have two function like this:
static int function2(lua_State *L) {
int i = luaL_checkint(L, 1);
/* do something */
return 1;
};
static int function1(lua_State *L) {
struct udata *u = luaL_checkudata(L, 1, UDATA_METATABLE);
int i = luaL_checkint(L, 2);
/* do something */
/* this does not work, first on call stack is udata, not int */
return function2(L);
};
The function call as above does not work. One option is to modify function2() to use the last element (index -1) on stack, but this is not a sollution in general since function2() might be called from various places with different call stacks.
Another way would be to replace the return function2(L); by
lua_pushcfunction(L, function2);
lua_pushvalue(L, 2);
lua_call(L, 1, 1); /* need to know number of results */
I assume this gives function2() its own call stack so there is no need to modify it. But this sollution seems overly complicated for functions with more parameters since it requires duplicating all of them on the stack.
tl;dr: What is the recommended way/a good way to call a lua_CFunction from inside another one?
In function1 you are expecting the bottom of the stack to be user data.
When you call function2 directly, the LuaState has not changed and therefore the bottom is still user data.
You can successfully call function2 from function1 by ensuring an integer is at index 1.
You could do this by calling lua_insert(L, 1) which will move the top (assuming index 2), to index 1.
You could also do this by popping all values the pushing the integer back on:
lua_pop(L, lua_gettop(L));
lua_pushnumber(L, i);
return function2(L);
lua_CFunction is not fully Lua function. It just a way to create Lua function/closure.
static int function1(lua_State *L) {
....
int top = lua_gettop(L);
lua_pushcfunction(L, function2);
lua_pushvalue(L, 2);
lua_call(L, 1, LUA_MULTRET);
return lua_gettop(L) - 1;
}
The Lua equivalent is
function function1(arg)
return (function(arg) --[[body of f2]] end)(arg)
end
So you create each time new function and call it.
For C function it is quite ok because it no need to compile and you do not need upvalue.
Also Lua 5.2 introduce light function for that.
But if you want equivalent for
int i = 1
local function function2(arg)
i = i + 1
...
end
function function1(arg)
return function2(arg)
end
You need a way to find real Lua function e.g. (not tested)
int f2_ref;
static int function1(lua_State *L) {
...
-- push `function2` on stack
lua_rawgeti(L, LUA_REGISTRYINDEX, f2_ref);
-- as above
}
static int function2(lua_State *L) {
int my_upvalue = lua_tonumber(L, lua_upvalueindex(1));
my_upvalue++;
lua_pushnumber(L, my_upvalue);
lua_replace(L, lua_upvalueindex(1));
...
}
int luaopen_foo(lua_State *L){
-- Here we create instance of Lua function(closure)
lua_pushnumber(L, 1);
lua_pushcclosure(L, function2, 1);
f2_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_pushcclosure(L, function1, 0);
return 1;
}
I'd say that calling it through lua is the recommended way to do that but if you don't want to do that for some reason then Timma's suggestions are the right ones.
From the documentation, I understand that a new thread created, must be
properly anchored before use.
To do that, I want to keep a reference to the new thread in the registry,
(Table[thread-addr] = thread) for that, I am doing this:
lua_State *L = NULL;
lua_State *L1 = NULL;
int tref = LUA_NOREF;
L = luaL_newstate(); // main lua thread/state
// create a table in registry: Table[thr-addr] = Thread
lua_newtable(L);
tref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_pop(L, 1);
L1 = lua_newthread(L);
// Anchor it
lua_rawgeti(L, LUA_REGISTRYINDEX, tref);
lua_pushnumber(L, (ptrdiff_t) L1);
lua_pushlightuserdata(L, L1);
lua_settable(L, -3);
Once I am done with the thread, I plan to set the Table[thread-addr] = nil
Is this sufficient ? or I should also set a meta-table to it, with weak keys/values ?
Thanks.
Once I am done with the thread, I plan to set the Table[thread-addr] = nil Is this sufficient? or I should also set a meta-table to it, with weak keys/values?
A weak table is used if you don't want objects it references to count as real 'strong' references. So Lua is allowed to GC an object if there are no other references to it even if the weak table still refers to that object.
From the use case you described, making Table weak probably isn't appropriate here since Lua might collect that coroutine object before you have a chance to use it.
Also your example code here:
L1 = lua_newthread(L);
// Anchor it
lua_rawgeti(L, LUA_REGISTRYINDEX, tref);
lua_pushnumber(L, (ptrdiff_t) L1);
lua_pushlightuserdata(L, L1);
lua_settable(L, -3);
the lua_pushlightuserdata function is meant for C pointers. The coroutine object lifetime won't get managed by Lua correctly if you tell it to treat the coroutine object like a C data pointer. For this you probably meant to use lua_pushthread instead.
I am trying to create a C function called Dfetch() that is registered in Lua as fetch(). I am looking for it to be tiered such that I can call dog.beagle.fetch() as the function from Lua. It just helps with the organization of the code better. Below is what I have, but it is not calling my C function. If I just do the global and not the subtable, the C function gets called. I am new to Lua, so I think I am just setting up the tables wrong.
void myregister(lua_State *L, const char *libname, const char *sublibname, const luaL_Reg *l)
{
luaL_newmetatable(L, libname);
lua_newtable(L); luaL_setfuncs(L,l,0);
lua_pushvalue(L,-1);
if(sublibname != NULL)
{
lua_newtable(L);
lua_setfield(L, -2, sublibname);
}
lua_setglobal(L, libname);
}
luaL_Reg fidofuncModule[] = { {"fetch", Dfetch}, {NULL, NULL}};
In my main(), I call the following:
lua_State * execContext = luaL_newstate();
//adding lua basic library
luaL_openlibs(execContext);
myregister(execContext, "dog", "beagle", fidofuncModule);
strcpy(buff, "dog.beagle.fetch();");
errorcode = luaL_loadbuffer(execContext, buff, strlen(buff), "line");
errorcode = lua_pcall(execContext, 0, 0, 0);
if (errorcode)
{
printf("%s", lua_tostring(execContext, -1));
lua_pop(execContext, 1); /* pop error message from the stack */
}
//cleaning house
lua_close(execContext);
Thanks, Tim
The Problem(s)
OK, let's walk through this function and the stack.
luaL_newmetatable(L, libname);
OK, the stack now contains a table from the metatable registry:
-1: table<libname>{possibly empty}
Next:
lua_newtable(L);
The stack now contains:
-1: table<new>{empty}
-2: table<libname>{possibly empty}
Next:
luaL_setfuncs(L,l,0);
Doesn't change the stack. But it does set a bunch of functions into the table.
-1: table<new>{bunch of functions}
-2: table<libname>{possibly empty}
Next:
lua_pushvalue(L,-1);
This copies the value on top of the stack. That's our table with a bunch of functions:
-1: table<new>{bunch of functions}
-2: table<new>{bunch of functions}
-3: table<libname>{possibly empty}
Next:
if(sublibname != NULL)
{
lua_newtable(L);
This creates a new table on the stack that's empty:
-1: table<new2>
-2: table<new>{bunch of functions}
-3: table<new>{bunch of functions}
-4: table<libname>{possibly empty}
Next:
lua_setfield(L, -2, sublibname);
This function, as stated in the documentation, sets a value into a table at the given table key name. The value is the value at the top of the stack, but the table it puts it in is the index.
So you just did this:
-1: table<new>{bunch of functions, sublibname=table<new2>}
-2: table<new>{bunch of functions, sublibname=table<new2>}
-3: table<libname>{possibly empty}
I'm pretty sure that's not what you wanted. I'll get to how to fix that when we continue.
Next:
}
lua_setglobal(L, libname);
This takes the top of the stack and sticks it in the global table, poping it off the top of the stack.
So the stack now looks like this:
-1: table<new>{bunch of functions, sublibname=table<new2>{empty}}
-2: table<libname>{possibly empty}
And the global table now has:
_G[libname] = table<new>{bunch of functions, sublibname=table<new2>{empty}}
So not only have you unbalanced the stack (more pushes than pops), you didn't get what you actually wanted. Plus, your metatable from the registry contains... nothing at all.
The Solution
So let's fix this. And let's do it properly.
Pretty much everything about what you tried to do is wrong. First, the only reason to do the subtable thing is so code like this will work:
myregister(execContext, "dog", "beagle", fidofuncModule);
myregister(execContext, "dog", "dane", danefuncModule);
This way, you can call dog.beagle and dog.dane. Well, to do that, myregister has to check the global table to see if there's already a dog table. If there is, it needs to store its stuff in there, and if not, it needs to create it. So your whole algorithm is kinda broken.
Also, presumably you want dog.beagle and dog.dane to both have their own fetch function. Well, the registry only has one place for a dog table, so if you use just libname for your luaL_newmetatable call, they'll be stomping on each others tables.
Here's how I would solve it. I have no idea if this works for what you're doing, but this is what I'd do.
First, forget the whole newmetatable nonsense; we work based on new tables, always. So we'll create the inner table and set functions onto it:
lua_newtable(L);
luaL_setfuncs(L,l,0);
So the stack looks like:
-1: table<new>{bunch of functions}
Next step, if we don't have a sub-library name, then we should set this directly into the global variable under libname and return:
if(!sublibname)
{
lua_setglobal(L, libname);
return;
}
That will pop the one value from the stack and set it in that location.
Since we do have a sub-library name, we need to store this table in the main table. If there's already a table in _G[libname], then we get that table. Otherwise, we create a new table and stick it into _G[libname].
lua_getglobal(L, libname);
if(lua_isnil(L, -1))
{
/*No table. Must create it and put it into the global*/
lua_newtable(L);
lua_pushvalue(L, -1); /*duplicate it on the stack*/
lua_setglobal(L, libname); /*pushes duplicate*/
}
At this point, our stack contains:
-1: table<libname>{possibly empty}
-2: table<new>{bunch of functions}
We then stick our created table into that one, using sublibname as the field:
lua_pushvalue(L, -2); /*duplicates our created table*/
lua_setfield(L, -2, sublibname);
Now the stack contains:
-1: table<libname>{stuff, sublibname=table<new>{bunch of functions}}
-2: table<new>{bunch of functions}
Since table is already in the global table (we either got it from there, or stored it there when we created it), we're done. So clean up the stack:
lua_pop(L, 2); /*balance the stack. Remove our junk from it*/
void myregister(lua_State *L, const char *libname, const char *sublibname, const luaL_Reg *lib)
{
// create 'libname' table
lua_newtable(L);
// no sublib: just import our library functions directly into lib and we're done
if (sublibname == NULL)
{
luaL_setfuncs(L, lib, 0);
}
// sublib: create a table for it, import functions to it, add to parent lib
else
{
lua_newtable(L);
luaL_setfuncs(L, lib, 0);
lua_setfield(L, -2, sublibname);
}
lua_setglobal(L, libname);
}
fetch is being registered into libname, not into sublibname. To confirm, add print(dog.fetch) to buff before the call.
Try this:
void myregister(lua_State *L, const char *libname, const char *sublibname, const luaL_Reg *l)
{
lua_newtable(L);
lua_pushvalue(L,-1);
lua_setglobal(L, libname);
if(sublibname != NULL)
{
lua_newtable(L);
lua_pushvalue(L,-1);
lua_setfield(L, -3, sublibname);
}
luaL_setfuncs(L,l,0);
}
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.
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);
}