从userdata在Objective C / C中创建Lua全局变量

时间:2012-04-07 17:03:57

标签: objective-c ios lua

我正在开发一个库,允许Lua(5.2)在iOS 5.x中编写游戏脚本。我创建了一个类并添加了绑定,以允许它从Lua创建和访问。从Lua调用的C初始化方法如下:

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;

}

这指定了一个在其他地方设置的元表,以提供对各种方法的访问。此外,它将表附加为用户值,以允许脚本代码为对象分配属性。

我可以在Lua脚本中创建这些对象,没有问题:

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

这里的objectTouched是一个处理触摸事件的其他地方定义的Lua方法。此处addEventListener将其绑定到touch个事件。

这些对象工作得很好。但是,当我尝试从C创建一个时,我遇到了问题。我可以创建对象,但尝试将其分配给全局,然后在脚本中调用它失败。

以下C代码运行

-(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));
}

这应该定义一个名为“Runtime”的全局。试图从像这样的脚本中访问这个变量

Runtime:addEventListener("enterFrame", enterFrame)

导致以下错误:

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

这是一个userdata值,但是当我直接在Lua中创建一个时,这似乎并不重要。 metatable绑定提供对方法和元方法的访问。同样,如果对象是从Lua创建的,只有在用C创建它时才能正常工作。

关于我在这里做错了什么的想法,或者从userdata创建全局的正确方法是什么?

修改

根据以下有关GEMINI_OBJECT_LUA_KEY混淆的评论,我想我会列出实际用于绑定的代码:

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;
}

此代码注册了为GeminiObjects提供方法和元方法的函数库(此处未显示)。对luaL_newmetatable的调用会创建一个新的元表,并将其与注册表GEMINI_OBJECT_LUA_KEY关联。 GEMINI_OBJECT_LUA_KEY只是标头中定义的唯一字符串。 luaL_setfuncs实际上将函数指针添加到元表中,使它们可用作对象的方法。

1 个答案:

答案 0 :(得分:2)

如果有人仍然感兴趣,我从Lua邮件列表上那些善良的人那里得到了我的问题的答案。这里的问题是在调用luaopen_geminiObjectLib之前没有调用库绑定函数addRuntimeObject

由于iOS不支持动态库,我通过向Lua源preloadedlibs中的linit.c数组添加指针,静态添加了我的库。不幸的是,在Lua脚本中执行require('libname')之前,不会加载以这种方式添加的库。由于我在执行Lua脚本之前调用了addRuntimeObject方法,因此尚未加载库。

解决方案是将指向luaopen_geminiObjectLib的指针添加到同一loadedlibs文件中的linit.c数组中。这导致在Lua启动时加载库而不需要脚本require