I am implementing a library in iOS to provided Lua (5.2) bindings for game programming. I have managed to successfully create several datatypes (if that is the correct word) using userdata, metatables, and uservalues. A typical constructor function is given below:
///////////// lines ///////////////////////////
static int newLine(lua_State *L){
NSLog(@"Creating new line...");
lineSetup(L);
NSLog(@"New line created.");
return 1;
}
lineSetup() is a C function created by factoring out common setup code:
void lineSetup(lua_State *L){
GeminiLine *line = [[GeminiLine alloc] initWithLuaState:L];
GeminiLine **lLine = (GeminiLine **)lua_newuserdata(L, sizeof(GeminiLine *));
*lLine = line;
luaL_getmetatable(L, GEMINI_LINE_LUA_KEY);
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
line.selfRef = luaL_ref(L, LUA_REGISTRYINDEX);
// append a lua table to this user data to allow the user to store values in it
lua_newtable(L);
lua_pushvalue(L, -1); // make a copy of the table becaue the next line pops the top value
// store a reference to this table so we can access it later
line.propertyTableRef = luaL_ref(L, LUA_REGISTRYINDEX);
// set the table as the user value for the Lua object
lua_setuservalue(L, -2);
}
Here GeminiLine is one of the Objective C types in my library. initWithLuaState: is an initializer that simply stores the pointer to the lua_State to be used later. It does not alter the state. GeminiLine objects store a reference to their user data obtained with the call to luaL_ref().
int luaopen_display_lib (lua_State *L){
// create meta tables for our various types /////////
// lines
luaL_newmetatable(L, GEMINI_LINE_LUA_KEY);
lua_pushvalue(L, -1);
luaL_setfuncs(L, line_m, 0);
/////// finished with metatables ///////////
// create the table for this library and popuplate it with pointers to our functions
luaL_newlib(L, displayLib_f);
return 1;
}
The call to luaL_newmetatable() is where the metatable is created and associated in the registry with my key, GEMINI_LINE_LUA_KEY. Note: this is a call to luaL_newmetatable, NOT a call to lua_newmetatable. The pointers to the object methods and metamethods and their string keys are given in the array line_m, not shown here. Similarly, the pointers to the functions for the library (the factory methods) and their keys are stored in the array displayLib_f, also not shown here.
For the most part this works great. I am able to create new objects in Lua scripts and access there methods/attributes, e.g.,
1 display = require('display')
2
3 line = display.newLine()
4 line.width = 3
etc. (line numbers shown)
When I try to create one of these objects directly in Objective C and bind it to a global variable, however, I run into problems. The following (admittedly contrived) example should work, but does not.
Creating an object in Objective C code after initializing Lua but before loading my script like this
-(void)createGlobal {
lineSetup(L);
// create an entry in the global table
lua_setglobal(L, "Runtime");
// clear the stack
lua_pop(L, lua_gettop(L));
}
should create a global called "Runtime", if I understand lua_setglobal() correctly. But running the following script afterwards
1 display = require('display')
2
3 line = display.newLine()
4 line.width = 3
5
6 x = Runtime
7 x.width = 4
generates the following error:
test.lua:7: attempt to index global 'x' (a userdata value)
My question is, why does line 4 work correctly, while line 7 complains about the global being userdata? What is the difference between the = assignment on line 3 and the lua_setglobal in my Objective C code?
Thanks in advance for any suggestions.
-James