2010-02-17 52 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++代碼特定部分進行交互。