связывание структур и ctor / dtor с tolua ++ - PullRequest
2 голосов
/ 15 февраля 2011

Допустим, я хочу связать фрагмент кода с Lua, который выглядит следующим образом:

typedef struct bar {
  void * some_data;
} bar;
bar * bar_create(void);
void bar_do_something(bar * baz);
void bar_free(bar * baz);

Я хочу создать эти объекты из сценария Lua, а не явно управлять их временем жизни. Желательно, чтобы мой сценарий написал

require "foo"
local baz = foo:bar()
baz:do_something()
baz = nil

Проблема : чтобы это работало как положено, мне нужно как-то сказать tolua ++ о bar_create и bar_free, являющемся конструктором / деструктором для bar. Как я могу это сделать? Для классов tolua ++ утверждает, что автоматически использует их ctor / dtor, но для структур?

Лучшее, что я могу придумать, это определение foo.pkg:

module foo {
  struct bar {
    static tolua_outside bar_create @ create();
    tolua_outside bar_do_something @ do_something();
    tolua_outside bar_free @ free();
  };
}

что означало бы, что я должен явно вызывать create () и free ().

1 Ответ

1 голос
/ 18 февраля 2011

Функции bar могут быть импортированы в Lua с помощью tolua ++ и упакованы для создания интерфейса в стиле объекта, включая сборку мусора.

Чтобы продемонстрировать передачу аргументов, я изменил интерфейс bar на

bar * bar_create(int x);
int bar_do_something(bar * baz, int y);
void bar_free(bar * baz);

и написали тестовую реализацию, которая печатает x, y и т. Д. При вызове функций.

Функция bar_create() Lua возвращает значение пользовательских данных. Lua освобождает такие пользовательские данные, вызывая метод __gc, хранящийся в метатаблице данных. Учитывая значение userdata и деструктор gc, метод __gc перезаписывается так, что сначала он вызывает gc, а затем вызывает оригинальный метод gc:

function wrap_garbage_collector(userdata, gc)
    local mt = getmetatable(userdata)
    local old_gc = mt.__gc
    function mt.__gc (data)
        gc(data)
        old_gc(data)
    end
end

Пользовательские данные одного типа имеют одинаковую метатабельность; поэтому функция wrap_garbage_collector() должна вызываться только один раз для каждого класса (при условии, что метатаблицы tolua ++ создаются один раз и освобождаются только при выходе).

В нижней части этого ответа находится полный файл bar.pkg, который импортирует функции bar и добавляет класс bar в модуль Lua с именем foo. Модуль foo загружается в интерпретатор ( см., Например, мой пример SO tolua ++ ) и используется следующим образом:

bars = {}

for i = 1, 3 do
    bars[i] = foo.bar(i)
end

for i = 1, 3 do
    local result = bars[i]:do_something(i * i)
    print("result:", result)
end

Реализация теста выводит, что происходит:

bar(1)
bar(2)
bar(3)
bar(1)::do_something(1)
result: 1
bar(2)::do_something(4)
result: 8
bar(3)::do_something(9)
result: 27
~bar(3)
~bar(2)
~bar(1)

Конструкция класса bar, представленная ниже, немного сложна: утилита build_class() возвращает класс (таблицу Lua), заданный конструктором, деструктором и методами класса. Корректировки, несомненно, понадобятся, но в качестве демонстрации прототипа пример должен быть в порядке.

$#include "bar.hpp"

// The bar class functions.
bar * bar_create(int x);
int bar_do_something(bar * baz, int y);
void bar_free(bar * baz);

$[
    -- Wrapping of the garbage collector of a user data value.
    function wrap_garbage_collector(userdata, gc)
        local mt = getmetatable(userdata)
        local old_gc = mt.__gc
        function mt.__gc (data)
            gc(data)
            old_gc(data)
        end
    end

    -- Construction of a class.
    --
    -- Arguments:
    --
    --   cons : constructor of the user data
    --   gc : destructor of the user data
    --   methods : a table of pairs { method = method_fun }
    --
    -- Every 'method_fun' of 'methods' is passed the user data 
    -- as the first argument.
    --
    function build_class(cons, gc, methods)
        local is_wrapped = false
        function class (args)
            -- Call the constructor.
            local value = cons(args)

            -- Adjust the garbage collector of the class (once only).
            if not is_wrapped then
                wrap_garbage_collector(value, gc)
                is_wrapped = true
            end

            -- Return a table with the methods added.
            local t = {}
            for name, method in pairs(methods) do
                t[name] =
                    function (self, ...)
                        -- Pass data and arguments to the method.
                        return (method(value, ...))
                    end
            end

            return t
        end
        return class
    end

    -- The Lua module that contains our classes.
    foo = foo or {}

    -- Build and assign the classes.
    foo.bar =
        build_class(bar_create, bar_free,
                    { do_something = bar_do_something })

    -- Clear global functions that shouldn't be visible.
    bar_create = nil
    bar_free = nil
    bar_do_something = nil
$]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...