在c中编写oo Lua接口的最佳方法是什么?

时间:2010-02-17 20:00:34

标签: c object lua

考虑以下示例(一个简单的2d向量lib)。这里有一个构造函数,它返回一个带有方法的对象表。我对这种方法的问题是它正在为每个构造函数创建新表。有没有办法使用表的单个实例,但只更改_data字段,该字段标识方法正在处理的点?这是一种更好的方法吗?

#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;
}

2 个答案:

答案 0 :(得分:6)

我刚刚浏览了你的代码,但看起来你的每个对象都是一个包含带有实例数据的轻量级userdatum的表,以及包含在Lua闭包中的一堆C函数。是的,这是非常低效的。第一个改进是不为每个实例使用相同的C函数创建单独的闭包。但我们可以做得比这更好。

无法做你想象的声音:你不能在对象之间共享一个表,但每个对象有一个不同的字段。如果一个字段不同,那么你有一个不同的表。

但是您可以以更高效的方式划分共享数据和实例数据。

无需创建表来保存实例数据。只需将分配的结构转换为(完整的,不是轻的)userdatum。这比桌子小一点。 (我认为在x86_64上有40个字节对64个字节;加上你分配的结构的大小。)

您将所有方法放入单个方法表中,并将该表与您返回的所有用户数据相关联。共享数据可以以各种方式与对象相关联。由于您希望在Lua中可以访问这些方法,因此您希望将方法表分配给M.__index,其中M是每个实例对象的元表。可以为所有对象分配一个共享的M,如果您愿意,M可以包含方法本身(然后M.__index将只是M)。

同样在metatable中,你需要放置一个__gc方法,以便在对象被垃圾收集时释放你分配的结构。

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

答案 1 :(得分:0)

考虑使用tolua,它允许您的lua代码直接与c / c ++代码的指定部分进行交互。