今天看了一篇分析lua++的文章,它提到在lua里访问c struct的成员的一个方法是,在c里给每个成员写一个函数,像name,就写成return tbl.name这样的,把这些getter放到metatable的一张表里(取名叫get),用"name"作为key。把metatable的__index属性指向一个函数,这个函数相当于一个中转站,根据缺失的key,调用get表里对应key的getter。
是很很巧妙的方法。之所以巧妙,是因为上面的操作,几乎所有的代码,都可以用程序来生成。只需要扫描和分析头文件。
lua++大概就是这么做的吧。
尝试了一晚上,差不多做成了一个demo,只是getter没有在c里写,直接在lua里写的。这样肯定是不妥的,但为了加快速度,只好...
下面是主要的代码:
-
wrapper.c
-
#include<stdio.h>
-
#include<lua.h>
-
#include<lualib.h>
-
#include<dog.h>
-
-
-
-
/*lua 似乎是这样,每次调用一个fucntion时,会清除栈上的参数和function*/
-
int getAttr(lua_State *L){
-
char *key = lua_tostring(L, -1);
-
-
lua_getmetatable(L, -2);
-
lua_pushstring(L, "get");
-
lua_gettable(L, -2); /**stack top:table= mtbl.get*/
-
lua_pushstring(L, key);
-
lua_gettable(L, -2); /**stack top:function = get.key*/
-
int err = lua_pcall(L, 0, 1, 0); /**核心在这里... ,暂时简化,规定get表的一堆getter都只返回1个integer*/
-
int getval;
-
if(err != 0){
-
//调用失败的话,lua_pcall会push一个error code。很好。
-
printf("can not find corresponding getter\n");
-
getval = -1;
-
}
-
else{
-
getval = lua_tointeger(L, -1);
-
-
}
-
lua_pop(L, 3);
-
lua_pushinteger(L, getval);
-
return 1;
-
}
-
/*prototype: userdata createDog(void)*/
-
int createDog(lua_State *L){
-
struct dog *dog = lua_newuserdata(L, sizeof(struct dog));
-
-
luaL_newmetatable(L, "mtbl");
-
lua_pushstring(L, "__index");
-
lua_pushcfunction(L, getAttr);/**你搞清楚,这里已经是把c函数注册到lua的一个key里了,不用再lua_register()*/
-
lua_settable(L, -3);
-
-
lua_pushstring(L, "get");
-
lua_newtable(L);
-
lua_settable(L, -3);
-
-
lua_setmetatable(L, -2);
-
-
return 1;
-
}
-
-
int luaopen_cmodule(lua_State *L){
-
lua_register(L, "createDog", createDog);
-
return 0;
-
}
-
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
-
> require"cmodule"
-
> d=createDog()
-
> mtbl=getmetatable(d)
-
> mtbl.get.age = function() return 1234 end
-
> print(d.agee)
-
can not find corresponding getter
-
-1
-
> print(d.age)
-
1234
-
>
阅读(1914) | 评论(0) | 转发(0) |