我正在开发一个库,允许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
实际上将函数指针添加到元表中,使它们可用作对象的方法。
答案 0 :(得分:2)
如果有人仍然感兴趣,我从Lua邮件列表上那些善良的人那里得到了我的问题的答案。这里的问题是在调用luaopen_geminiObjectLib
之前没有调用库绑定函数addRuntimeObject
。
由于iOS不支持动态库,我通过向Lua源preloadedlibs
中的linit.c
数组添加指针,静态添加了我的库。不幸的是,在Lua脚本中执行require('libname')
之前,不会加载以这种方式添加的库。由于我在执行Lua脚本之前调用了addRuntimeObject
方法,因此尚未加载库。
解决方案是将指向luaopen_geminiObjectLib
的指针添加到同一loadedlibs
文件中的linit.c
数组中。这导致在Lua启动时加载库而不需要脚本require
。