2010-02-17 5 views
0

Рассмотрим следующий пример (простой 2d векторный lib). Здесь есть одна функция-конструктор, которая возвращает таблицу объектов с помощью методов. Моя проблема с этим подходом заключается в том, что он создает новые таблицы с каждым конструктором. Есть ли способ использовать один экземпляр таблицы, но изменить только поле _data, которое идентифицирует обрабатываемую точку методами? Это лучший подход?Лучший подход к написанию oo Интерфейсы Lua в c?

#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 с данными экземпляра, а также кучу C-функций, завернутых в блокировки Lua. Да, это очень неэффективно. Первым улучшением было бы не создавать отдельное закрытие для использования каждым экземпляром одной и той же функции С. Но мы можем сделать гораздо лучше, чем это.

не может делать то, что кажется вам, вы воображаете: у вас не может быть единой таблицы, разделяемой между объектами, но с одним полем, различным для каждого объекта. Если одно поле отличается от другого, у вас есть другая таблица.

Но вы можете разделить общие данные и данные экземпляра способами, которые являются более эффективными.

Нет необходимости создавать таблицу для хранения данных экземпляра. Просто сделайте выделенную структуру в (полный, а не легкий) userdatum. Это немного меньше, чем таблица. (Я думаю, 40 байт против 64 байт на x86_64 плюс размер вашей выделенной структуры.)

Вы поместите все свои методы в одну таблицу методов и сопоставьте одну таблицу со всеми возвращаемыми пользовательскими данными. Общие данные могут быть связаны с объектами различными способами. Поскольку вы хотите, чтобы методы были доступны в Lua, вам нужно, чтобы таблица методов была назначена M.__index, где M является метатемой каждого экземпляра объекта. Всем объектам может быть назначен один общий M, а M может содержать сами методы, если вам нравится (тогда M.__index будет просто M).

Также в metatable вы хотите поместить метод __gc, чтобы освободить выделенную структуру, когда объект собран из мусора.

Посмотрите на the chapter of Progamming in Lua on userdata. Также взгляните на пакет lv3 на lhf's site. (Он один из авторов Lua.)

+1

Супер хороший ответ. +1 –

0

Рассмотрите возможность использования tolua, который позволяет ваш код Lua непосредственно взаимодействовать с определенными участками вашего C/C++ кода.

Смежные вопросы