代码之家  ›  专栏  ›  技术社区  ›  Nick Van Brunt

用C语言编写OO-Lua接口的最佳方法?

  •  0
  • Nick Van Brunt  · 技术社区  · 15 年前

    考虑下面的例子(一个简单的二维向量库)。这里有一个构造函数函数,它用方法返回一个对象表。我对这种方法的问题是它正在用每个构造函数创建新表。是否有一种方法可以使用表的单个实例,但只更改标识方法正在处理的点的数据字段?这是更好的方法吗?

    #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 回复  |  直到 15 年前
        1
  •  6
  •   dubiousjim    15 年前

    我刚刚略读了您的代码,但看起来您的每个对象都是一个表,其中包含一个包含实例数据的轻量级用户数据,以及一组封装在Lua闭包中的C函数。是的,这是非常低效的。第一个改进是不为每个实例使用相同的C函数而单独关闭。但我们甚至可以做得更好。

    不能 按照您想象的那样做:不能在对象之间共享一个表,而是为每个对象使用一个不同的字段。如果一个字段不同,那么您就有了一个不同的表。

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

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

    您将把所有方法放到一个方法表中,并将该表与返回的所有用户数据相关联。共享数据可以通过多种方式与对象关联。因为您希望在lua中可以访问这些方法,所以需要将方法表分配给 M.__index 在哪里 M 是每个实例对象的元表。所有对象都可以分配一个共享的m,如果您愿意,m可以包含方法本身(然后 M.S.指数 只有m)。

    另外,在元表中,您还需要 __gc 方法,以便在对象被垃圾收集时释放所分配的结构。

    看一看 the chapter of Progamming in Lua on userdata . 还可以查看lv3包 lhf's site . (他是Lua的作者之一。)

        2
  •  0
  •   DShook    15 年前

    考虑使用 tolua 它允许您的Lua代码与C/C++代码的指定部分直接交互。