使用 tolua++ 绑定结构体和 ctor/dtor

发布于 2024-10-17 13:30:23 字数 745 浏览 7 评论 0原文

假设我想将一段代码绑定到 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() 。

Let's say I want to bind a piece of code to Lua that looks like this:

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

I want to create these objects from a Lua script, and not explicitly manage their lifetime. Prefereably, I would like my script to write

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

Problem: For that to work as expected, I need to somehow tell tolua++ about bar_create and bar_free being the constructor/destructor for bar. How do I do that? For classes, tolua++ claims to automatically use their ctor/dtor, but for structs?

The best thing I can come up with is this definition of foo.pkg:

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

which would mean I have to call create() and free() explicitly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

迷路的信 2024-10-24 13:30:24

可以使用 tolua++ 将 bar 函数导入到 Lua 中,并进行包装以生成对象样式的接口,包括垃圾收集。

为了演示参数的传递,我将 bar 接口更改为

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

并编写了一个测试实现,在以下情况下打印出 xy 等函数被调用。

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

相同类型的Userdata共享相同的元表;因此,对于每个类,wrap_garbage_collector() 函数应该只调用一次(假设 tolua++ 的元表构造一次并仅在退出时释放)。

在这个答案的底部是一个完整的 bar.pkg 文件,它导入 bar 函数并将一个 bar 类添加到名为 <代码>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
$]

The bar functions can be imported into Lua using tolua++ and wrapped to yield an object-style interface, garbage collection included.

To demonstrate the passing of arguments, I have changed the bar interface to

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

and written a test implementation that prints out x, y, etc. when the functions are called.

The bar_create() Lua function returns a userdata value. Lua deallocates such user data by calling the __gc method stored in the metatable of the data. Given a userdata value and a destructor gc, the __gc method is overwritten such that it first calls gc and then calls the original gc method:

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

Userdata of the same type share the same metatable; therefore the wrap_garbage_collector() function should be called only once for each class (assuming that tolua++'s metatables are constructed once and deallocated only at exit).

At the bottom of this answer is a complete bar.pkg file that imports the bar functions and adds a bar class to a Lua module named foo. The foo module is loaded into the interpreter (see for example my SO tolua++ example) and used like this:

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

The test implementation prints out what happens:

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)

The construction of the bar class below is a little elaborate: the build_class() utility returns a class (a Lua table) given the constructor, destructor, and the class methods. Adjustments will no doubt be needed, but as a prototype demonstration the example should be OK.

$#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
$]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文