lua与c++的class绑定

来源:互联网 发布:java中的命令模式 编辑:程序博客网 时间:2024/06/06 17:00

一个naive的方法

static int newSprite(lua_State *L) {    int n = lua_gettop(L);  // Number of arguments    if (n != 4)         return luaL_error(L, "Got %d arguments expected 4", n);     double x = luaL_checknumber (L, 1);          double y = luaL_checknumber (L, 2);     double dir = luaL_checknumber (L, 3);          double speed = luaL_checknumber (L, 4);     Sprite *s = new Sprite(Point2(x, y), dir, speed);    lua_pushlightuserdata(L, s);  return 1; }

非常简单的一个实现,可惜它并不能工作,因为lightuserdata只是一个指针,但是这东西在Lua脚本里并不被识别,也就不能直接使用。

一个基本符合要求的方法

既然不能直接使用lightuserdata,那么我们就使用user data数据嘛。它是带metatable的。

static int newSprite(lua_State* l) {    int n = lua_gettop(l);    if(n != 2)        return luaL_error(l, "Got %d arguments expected 2", n);    Sprite **s = (Sprite **)lua_newuserdata(l, sizeof(Sprite *));    int x = luaL_checknumber(l, 1);    int y = luaL_checknumber(l, 2);    *s = new Sprite(x, y);    lua_getglobal(l ,"Sprite");    lua_setmetatable(l, -2);    return 1;}

这里我们可以看到,它将全局变量的Sprite作为metatable绑定到新建的userdata元素(即是Sprite指针)。这个只是创建Sprite的绑定函数

static const luaL_Reg gSpriteFuncs[] = {    {"new", newSprite},    {"getx", spriteX},    {NULL, NULL}};void registerSprite(lua_State *l){    luaL_register(l, "Sprite", gSpriteFuncs); //注册到Sprite Table      lua_pushvalue(l,-1);  //    lua_setfield(l, -2, "__index");    }

接着我们定义绑定表。下面的registerSprite函数就是在指定的lua环境里,实现注册Sprite类。
首先注册了一个函数集到Sprite Table,这就是上面使用的Sprite全局变量。然后再复制一个Table,作为自身的__index属性。

local sprite = Sprite.new(1, 2)local posx = sprite:getx()print(posx)

所以Sprite.new()可以就是调用newSprite创建类实例,然后类实例会绑定Sprite作为自己的metatable,因为userdata不是table,所以__index是永远触发的,因为metatable是table,不是function,所以触发就会在metatable里找对应的元素,比如getx。然后它就会找到newSprite这个function元素。因为是通过”:”来触发的,所以会存在一个userdata的参数。

一个绝对完善的方法

上面的方法基本可以用的,但是如果你在Lua改变了一个sprite实例的方法,那么所有的sprite实例方法都会被改变。
在registry里创建一个表,叫做“Lusion.Sprite”,里面注册一堆函数。在复制一份这个metatable,将其作为其本身的__index属性。再重复注册一个Sprite函数集。

Lusion.Sprite(Funcs)->__index->Lusion.Sprite(Funcs)

在newSprite的时候,第一个参数是一张表,其实就是Sprite函数集的表。我们新建一张表,将Sprite作为它的metatable,并将__index属性设成Sprite自己。然后就新建一个userdata,获得Lusion.Sprite,把它作为userdata的metatable。把早前建的新表的__self域指向userdata,将新表返回。结果如下所示:

table-->metatable->Sprite->__index->table       |->__self->userdata->metatable->Lusion.Sprite

读取的时候,就先通过table获得__self的值,然后通过self的值读取userdata,获得指针
这种方法为什么可以解决上面的问题呢。因为在new实例的时候,table是新建的,metatable里面保存了自己的函数。我还不是很理解为什么Lusion.Sprite需要链接自己,说不定是为什么destroy的时候用的。

原创粉丝点击