简体   繁体   中英

Creating Lua globals in Objective C/C from userdata

I am working on a library to allow Lua (5.2) scripting of games in iOS 5.x. I have created a class and added bindings to allow it to be created and accessed form Lua. The C initializer method called from Lua is given below:

static int newGeminiObject(lua_State *L){
    GeminiObject *go = [[GeminiObject alloc] initWithLuaState:L];

    GeminiObject **lgo = (GeminiObject **)lua_newuserdata(L, sizeof(GeminiObject *));
    *lgo = go;

    luaL_getmetatable(L, GEMINI_OBJECT_LUA_KEY);
    lua_setmetatable(L, -2);

    lua_newtable(L);
    lua_setuservalue(L, -2);

    NSLog(@"New GeminiObject created");

    // add this new object to the globall list of objects
    [[Gemini shared].geminiObjects addObject:go];

    return 1;

}

This assigns a metatable which is set up elsewhere to provide access to various methods. Additionally, it attaches a table as a uservalue to allow script code to assign attributes to the objects.

I can create these objects in Lua scripts with no problem:

require "gemini"
x = gemini.new()
x:addEventListener("touch", objectTouched)

Here objectTouched is aa Lua method defined elsewhere that handles a touch event. Here addEventListener binds it to touch events.

These objects work just fine. When I attempt to create one from C, however, I am running into problems. I can create the object, but trying to assign it to a global and then invoke it in a script fails.

The following C code runs

-(void) addRuntimeObject {
    GeminiObject *rt = [[GeminiObject alloc] initWithLuaState:L];
    GeminiObject **lruntime = (GeminiObject **)lua_newuserdata(L, sizeof(GeminiObject *));
    *lruntime = rt;

    // set the metatable - effectively declaring the type for this object
    luaL_getmetatable(L, GEMINI_OBJECT_LUA_KEY);
    lua_setmetatable(L, -2);

    // add a table to hold anything the user wants to add
    lua_newtable(L);
    lua_setuservalue(L, -2);

    // create an entry in the global table
    lua_setglobal(L, "Runtime");

    // empty the stack
    lua_pop(L, lua_gettop(L));
}

This should define a global named "Runtime". Trying to access this variable from a script like this

Runtime:addEventListener("enterFrame", enterFrame)

Results in the following error:

attempt to index global 'Runtime' (a userdata value)

It is a userdata value, but this doesn't seem to matter when I create one in Lua directly. The metatable binding provides access to the methods and metamethods. Again, this works fine if the object is created from Lua, just not when it is created in C.

Any ideas as to what I'm doing wrong here, or what the correct way to make a global from userdata is?

EDIT

Based on comments below regarding confusion about GEMINI_OBJECT_LUA_KEY, I thought I would list the code that is actually used in the binding:

static const struct luaL_Reg geminiObjectLib_f [] = {
    {"new", newGeminiObject},
    {NULL, NULL}
};

static const struct luaL_Reg geminiObjectLib_m [] = {
    {"addEventListener", addEventListener},
    {"__gc", geminiObjectGC},
    {"__index", l_irc_index},
    {"__newindex", l_irc_newindex},
    {NULL, NULL}
};

int luaopen_geminiObjectLib (lua_State *L){
    // create the metatable and put it into the registry
    luaL_newmetatable(L, GEMINI_OBJECT_LUA_KEY);

    lua_pushvalue(L, -1); // duplicates the metatable


    luaL_setfuncs(L, geminiObjectLib_m, 0);

    // create a table/library to hold the functions
    luaL_newlib(L, geminiObjectLib_f);

    NSLog(@"gemini lib opened");

    return 1;
}

This code registers the library of functions (not show here) that provide the methods and metamethods for the GeminiObjects . The call to luaL_newmetatable creates a new metatable and associates it in the registry with the key GEMINI_OBJECT_LUA_KEY . GEMINI_OBJECT_LUA_KEY is just a unique string defined in the header. luaL_setfuncs actually adds the function pointers to the metatable, making them available as methods of the objects.

In case anyone is still interested, I got the answer to my question from the kind folks on the Lua mailing list. The problem here is that the library binding function luaopen_geminiObjectLib is not called before my call to addRuntimeObject .

Since iOS does not support dynamic libraries, I had added my libraries in statically by adding pointers to them to the preloadedlibs array in linit.c of the Lua source. Unfortunately, libraries added this way are not loaded until require('libname') is executed in a Lua script. Since I was calling my addRuntimeObject method prior to executing the Lua script, the library was not yet loaded.

The solution is to add the pointer to luaopen_geminiObjectLib to the loadedlibs array in the same linit.c file. This causes the library to be loaded when Lua starts up with no need for scripts to require it.

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