Лучший подход к написанию Luo интерфейсов в C? - PullRequest
0 голосов
/ 17 февраля 2010

Рассмотрим следующий пример (простая двухмерная векторная библиотека). Здесь есть одна функция конструктора, которая возвращает таблицу объектов с методами. Моя проблема с этим подходом заключается в том, что он создает новые таблицы с каждым конструктором. Есть ли способ использовать один экземпляр таблицы, но изменить только поле _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 ]

6 голосов
/ 17 февраля 2010

Я только что просмотрел ваш код, но похоже, что каждый из ваших объектов представляет собой таблицу, содержащую легкий userdatum с данными экземпляра, а также набор функций C, заключенных в замыкания Lua.Да, это очень неэффективно.Первое улучшение будет состоять в том, чтобы не делать отдельное закрытие для использования каждым экземпляром одной и той же функции C.Но мы можем сделать намного лучше, чем даже это.

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

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

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

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

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

Посмотрите главу Progamming в Lua на userdata .Также взгляните на пакет lv3 на сайте lhf .(Он один из авторов Lua.)

0 голосов
/ 17 февраля 2010

Попробуйте использовать tolua , которая позволяет вашему коду lua напрямую взаимодействовать с указанными частями вашего кода c / c ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...