|
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(). In addition to assigning a metatable to the new object I am attaching a Lua table as a uservalue to allow users to store attribute information for the object. A reference to this table is also stored with the GeminiLine object. The metatable is created when the library is opened and bound in the registry to a constant string key, GEMINI_LINE_LUA_KEY, defined in the header. The function to open the library is given here: 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 |