2010-02-17 19 views
0

考虑下面的例子(一个简单的2D矢量库)。这里有一个构造函数,它返回一个包含方法的对象表。这个方法的问题在于它使用每个构造函数创建新表。有没有办法使用表的单个实例,但只更改_data字段,该字段标识方法正在处理的点?这是一个更好的方法吗?在c中写入oo Lua接口的最佳方法?

#include <stdlib.h> 
#include <assert.h> 
#include <stdio.h> 
#include "lua.h" 
#include "lauxlib.h" 
#include "lualib.h" 

const char* test = 
"p1 = point(410, 680);" 
"p2 = point(320, 120);" 
"print('dot='..p1:dot(p2));" 
"print('cross='..p1:cross(p2));"; 

typedef struct point_t { 
    lua_Number x, y; 
} point_t; 

point_t* p_new(lua_Number x, lua_Number y) { 
    point_t* p = malloc(sizeof(point_t)); 
    p->x = x; 
    p->y = y; 
    return p; 
} 

void lua_settabledata(lua_State *L , char * key , void * value) { 
    lua_pushstring(L, key); 
    lua_pushlightuserdata(L, value); 
    lua_settable(L, -3); 
} 

void lua_settablefunction(lua_State *L, char * key , lua_CFunction value) { 
    lua_pushstring(L, key); 
    lua_pushcfunction(L, value); 
    lua_settable(L, -3); 
} 

point_t* lua_topoint(lua_State *L, int index) { 
    point_t* p; 
    lua_pushstring(L, "_data"); 
    lua_gettable(L, index); 
    p = lua_touserdata(L, -1); 
    lua_pop(L, 1); 
    assert(p); 
    return p; 
} 

int l_dot(lua_State *L) { 
    point_t* p1 = lua_topoint(L, 1); 
    point_t* p2 = lua_topoint(L, 2); 
    lua_pushnumber(L, p1->x*p2->x + p1->y*p2->y); 
    return 1; 
} 

int l_cross(lua_State *L) { 
    point_t* p1 = lua_topoint(L, 1); 
    point_t* p2 = lua_topoint(L, 2); 
    lua_pushnumber(L, p1->x*p2->y - p1->y*p2->x); 
    return 1; 
} 

int l_setx(lua_State *L) { 
    point_t* p = lua_topoint(L, 1); 
    p->x = lua_tonumber(L, 2); 
    return 0; 
} 

int l_sety(lua_State *L) { 
    point_t* p = lua_topoint(L, 1); 
    p->y = lua_tonumber(L, 2); 
    return 0; 
} 

int l_getx(lua_State *L) { 
    point_t* p = lua_topoint(L, 1); 
    lua_pushnumber(L, p->x); 
    return 1; 
} 

int l_gety(lua_State *L) { 
    point_t* p = lua_topoint(L, 1); 
    lua_pushnumber(L, p->y); 
    return 1; 
} 

int l_point(lua_State* L) { 
    lua_Number x = lua_tonumber(L, 1); 
    lua_Number y = lua_tonumber(L, 2); 
    lua_newtable(L); 
    lua_settabledata(L , "_data", p_new(x, y)); 
    lua_settablefunction(L , "dot", l_dot); 
    lua_settablefunction(L , "cross", l_cross); 
    lua_settablefunction(L , "setx", l_setx); 
    lua_settablefunction(L , "sety", l_sety); 
    lua_settablefunction(L , "getx", l_getx); 
    lua_settablefunction(L , "gety", l_gety); 
    return 1; 
} 

int main() { 
    lua_State* L = lua_open(); 
    luaL_openlibs(L); 
    lua_register(L, "point", l_point); 
    if (luaL_loadstring(L, test) || lua_pcall(L, 0, 0, 0)) 
     printf("error: %s", lua_tostring(L, -1)); 
    getchar(); 
    return 0; 
} 

回答

6

我刚刚掠过你的代码,但它看起来像每个对象是含有光的userdatum与实例数据,以及一堆包裹成的Lua闭包的C函数表。是的,这是非常低效的。第一个改进是不为每个实例使用相同的C函数单独关闭。但是,即便如此,我们仍然可以做得更好。

不能做它听起来像你想象的那样:你不能在对象之间共享一个表,但每个对象有一个不同的字段。如果一个字段不同,那么你有一个不同的表。

但你可以在更有效的方式瓜分共享数据和实例数据。

有没有必要创建一个表来保存您的实例数据。只需将你的分配结构变成一个(完整的,不轻的)userdatum。这比表格小一点。 (我认为x86_64上的40个字节与64个字节;加上分配的结构的大小。)

您会将所有方法放入单个方法表中,并将该表与所有返回的用户数据相关联。共享数据可以通过多种方式与对象相关联。既然你想要在Lua中访问这些方法,你需要将方法表分配到M.__index,其中M是每个实例对象的元数据。所有的对象都可以分配一个共享的M,如果你喜欢,M可以包含方法本身(那么M.__index就是M)。

此外,在元表,你会想要把一个__gc方法,释放你的分配结构当对象被垃圾回收。

看一看the chapter of Progamming in Lua on userdata。也请看看lhf's site的lv3软件包。 (他是Lua作者之一)。

+1

超好的答案。 +1 –

0

考虑使用tolua其允许你的Lua代码直接与您的C/C++代码特定部分进行交互。