如果你想要真正独立的绑定,这里是经典的 C 风格样板。只需将“Object”替换为您的类名并实现// { } 块。将push_Object 从静态转为公共将为您提供通用的现有对象推送器,该对象推送器还将对象缓存在元表中(否则多次推送会创建许多不同的用户数据,并带有明显的 gc 问题)。
如果你愿意,你也可以 C++ify 或 library-fy,但我个人不这样做,因为添加例如__newindex-to-environment 代理和其他怪癖如果是库就不会那么简单了。实际上,所有样板都在push_mt、push_Object、forget_Object 和check_Object 中,其他的都需要微调。
请注意,这只会绑定一个单个类,而不是一次绑定所有类。
// { class Object { ... } }
static const char *tname = "Object";
static void push_Object(lua_State *L, Object *object);
static Object *check_Object(lua_State *L, int i);
static int
l_gc(lua_State *L)
{
Object **ud = luaL_checkudata(L, 1, tname);
if (*ud) {
// { delete *ud }
*ud = NULL;
}
return 0;
}
static int
l_tostring(lua_State *L)
{
Object **ud = luaL_checkudata(L, 1, tname);
lua_pushfstring(L, "%s: %p", tname, *ud);
return 1;
}
static int
l_new(lua_State *L)
{
Object *object = NULL; // { = new Object }
push_Object(L, object);
return 1;
}
static int
l_method(lua_State *L)
{
Object *object = check_Object(L, 1);
lua_Integer int_arg = luaL_checkinteger(L, 2);
const char *str_arg = luaL_checklstring(L, 3, NULL);
// { object->method(int_arg, str_arg) }
return 0;
}
static const luaL_Reg lib[] = {
// functions
{ "new", l_new }, // () -> object
// methods
{ "method", l_method }, // (object, int, string) -> none
{ NULL, NULL },
};
static lua_CFunction first_m = l_method;
static void
push_mt(lua_State *L)
{
if (luaL_newmetatable(L, tname)) {
size_t m = 0; while (first_m != lib[m].func) m++;
lua_createtable(L, 0, 0);
luaL_register(L, NULL, &lib[m]);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, l_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, l_gc);
lua_setfield(L, -2, "__gc");
lua_pushstring(L, tname);
lua_setfield(L, -2, "__metatable");
// mt.objects = setmetatable({ }, { __mode = "v" })
lua_createtable(L, 0, 0);
lua_createtable(L, 0, 1);
lua_pushstring(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "objects");
}
}
static void
push_Object(lua_State *L, Object *object)
{
int top = lua_gettop(L);
push_mt(L);
lua_getfield(L, -1, "objects");
// top+1 = mt
// top+2 = mt.objects
// ud = mt.objects[object]
lua_pushlightuserdata(L, object);
lua_gettable(L, top+2);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
Object **ud = lua_newuserdata(L, sizeof(*ud));
*ud = object;
// setmetatable(ud, mt)
lua_pushvalue(L, top+1);
lua_setmetatable(L, -2);
// mt.objects[object] = ud
lua_pushlightuserdata(L, object);
lua_pushvalue(L, -3);
lua_pushvalue(L, top+2);
}
// return ud
lua_replace(L, top+1);
lua_settop(L, top+1);
return; // ud at top
}
static void
forget_Object(lua_State *L, Object *object)
{
int top = lua_gettop(L);
push_mt(L);
lua_getfield(L, -1, "objects");
// top+1 = mt
// top+2 = mt.objects
// ud = mt.objects[object]
lua_pushlightuserdata(L, object);
lua_pushnil(L);
lua_settable(L, top+2);
lua_settop(L, top);
}
static Object *
check_Object(lua_State *L, int i)
{
Object **ud = luaL_checkudata(L, i, tname);
Object *object = *ud;
if (object == NULL)
luaL_error(L, "%s is finalized", tname);
return object;
}
int
luaopen_Object(lua_State *L)
{
push_mt(L); // register tname
lua_createtable(L, 0, sizeof(lib)-1);
luaL_register(L, NULL, lib);
return 1;
}