我有一個Actor
,我想給一個Script
。如何在C中的Lua模塊內創建一個Lua模塊?
而不是有多個共享對象,我想有一個單獨的頂級模塊,其中包含自己的依賴關係。
換句話說,我希望能夠做到這一點:
Actor = require 'Actor'
Script = require 'Actor.Script'
script = Script("To be, or not to be ...");
actor = Actor();
這些都只是返回的同時創建Actor
和Actor.Script
類型的userdata
功能。我的問題是,雖然我可以加載此代碼,但它不能按預期工作。看起來Script
只是簡單地返回一個Actor
userdata
。
print(script) => Actor 0x7fb7a240e998
print(actor) => Actor 0x7fb7a240a478
我期待:
print(script) => Actor.Script 0x7fb7a240e998
print(actor) => Actor 0x7fb7a240a478
如果我「胸圍出」的代碼轉換成兩個不同的模塊,我得到預期的結果,但我真的喜歡這個單一模塊中。
我在OSX和鏘編譯具有:
clang -Wall -I./ -I/usr/local/include/ -bundle -undefined dynamic_lookup actor.c script.c -o Actor.so -L./ -L/usr/local/lib
這裏是我使用的代碼:
actor.c:
#include "common.h"
#define ACTOR_LIB "Actor"
typedef struct Actor {
struct Script *script;
} Actor;
/*
* Allocate, initialize, and push a new Actor onto the stack.
* Returns a pointer to that Actor.
*/
Actor*
lua_newactor (lua_State *L)
{
Actor *actor = lua_malloc(L, sizeof(Actor));
actor->script = NULL;
return actor;
}
/*
* Make sure the argument at index N is a actor and return it if it is.
*/
Actor*
lua_checkactor (lua_State *L, int index)
{
return (Actor *) luaL_checkudata(L, index, ACTOR_LIB);
}
static int
actor_new (lua_State* L)
{
Actor *actor = lua_newactor(L);
lua_pushobject(L, actor, ACTOR_LIB);
return 1;
}
static int
actor_print (lua_State* L)
{
Actor *actor = lua_checkactor(L, 1);
lua_pushfstring(L, "%s %p", ACTOR_LIB, actor);
return 1;
}
static const luaL_Reg actor_methods[] = {
{"__tostring", actor_print},
{ NULL, NULL }
};
int
luaopen_Actor (lua_State * L)
{
/* create metatable */
luaL_newmetatable(L, ACTOR_LIB);
/* metatable.__index = metatable */
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
/* register methods */
luaL_setfuncs(L, actor_methods, 0);
/* Actor() => new Actor */
lua_pushcfunction(L, actor_new);
return 1;
}
script.c:
#include "common.h"
#define SCRIPT_LIB "Actor.Script"
typedef struct Script {
const char *string;
} Script;
/*
* Allocate a new Script to be passed around.
*/
Script *
lua_newscript (lua_State *L, const char *string)
{
if (string == NULL)
luaL_error(L, "`string` cannot be empty!");
Script *script = (Script*) lua_malloc(L, sizeof(Script));
script->string = string;
return script;
}
/*
* Make sure the argument at index N is a Script and return it if it is.
*/
Script *
lua_checkscript (lua_State *L, int index)
{
return (Script *) luaL_checkudata(L, index, SCRIPT_LIB);
}
static int
script_new (lua_State *L)
{
const char *filename = luaL_checkstring(L, 1);
Script *script = lua_newscript(L, filename);
lua_pushobject (L, script, SCRIPT_LIB);
return 1;
}
static int
script_print (lua_State* L)
{
Script *script = lua_checkscript(L, 1);
lua_pushfstring(L, "%s %p", SCRIPT_LIB, script);
return 1;
}
static const luaL_Reg script_methods[] = {
{"__tostring", script_print},
{ NULL, NULL }
};
int
luaopen_Actor_Script (lua_State *L)
{
/* create metatable */
luaL_newmetatable(L, SCRIPT_LIB);
/* metatable.__index = metatable */
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
/* register methods */
luaL_setfuncs(L, script_methods, 0);
/* Push a function: Script(...) => new script */
lua_pushcfunction(L, script_new);
return 1;
}
common.h:
#ifndef COMMON
#define COMMON
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
/*
* Allocates size_t memory on the given Lua state.
* lua_newuserdata automatically pushes it, so we pop it.
*/
static
void *
lua_malloc (lua_State *L, size_t size)
{
void *p = lua_newuserdata(L, size);
lua_pop(L, 1);
return p;
}
/*
* Associate an object pointer with given name and push that onto the stack.
*/
static
void
lua_pushobject (lua_State *L, void *object_pointer, const char *metatable_name)
{
lua_pushlightuserdata(L, object_pointer);
luaL_getmetatable(L, metatable_name);
lua_setmetatable(L, -2);
}
#endif
不要立即彈出由'lua_newuserdata()'創建的值,它是阻止您的用戶數據被垃圾收集的唯一因素。而且,所有light userdatas都共享一個metatable,這就是爲什麼所有懸掛的指針^ W^W「對象」都是相同類型的原因。 – siffiejoe
@siffiejoe你能指出我在正確的方向爲C創建多個對象的Lua和metatables如何工作? – Leroy
只需擺脫common.h中的'lua_pop'和'lua_pushlightuserdata',並且你的代碼應該可以工作。你唯一的問題是,當你使用完整的userdata時,你使用lightuserdata。 – siffiejoe