0

我正在开发一个库以允许在 iOS 5.x 中编写游戏的 Lua (5.2) 脚本。我创建了一个类并添加了绑定以允许从 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:addEventListener("enterFrame", enterFrame)

导致以下错误:

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

这是一个 userdata 值,但是当我直接在 Lua 中创建一个时,这似乎并不重要。元表绑定提供对方法和元方法的访问。同样,如果对象是从 Lua 创建的,这很好用,而不是在 C 中创建时。

关于我在这里做错了什么的任何想法,或者从用户数据创建全局的正确方法是什么?

编辑

基于以下关于 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创建一个新的元表并将其在注册表中与 key 相关联GEMINI_OBJECT_LUA_KEYGEMINI_OBJECT_LUA_KEY只是在标头中定义的唯一字符串。 luaL_setfuncs实际上将函数指针添加到元表,使它们可用作对象的方法。

4

1 回答 1

2

如果有人仍然感兴趣,我从 Lua 邮件列表上的好心人那里得到了我的问题的答案。这里的问题是luaopen_geminiObjectLib在我调用addRuntimeObject.

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

解决方案是在同一个文件luaopen_geminiObjectLib中添加指向数组的指针。这会导致在 Lua 启动时加载库而无需脚本。loadedlibslinit.crequire

于 2012-04-10T23:34:43.577 回答