2011-02-15 84 views
2

構造函數/析構函數比方說,我想了一段代碼到Lua看起來像這樣綁定:結合結構與tolua ++

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

我想創建一個Lua腳本,這些對象,而沒有明確管理他們的一生。 Prefereably,我想我的劇本寫

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

問題:對於按預期方式工作,我需要以某種方式告訴tolua ++約bar_create和bar_free是用於酒吧的構造/析構函數。我怎麼做?對於課程,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(); 
    }; 
} 

這意味着我必須調用創建()和free()明確。

+0

我目前想添加缺少的功能自己的,但是這意味着我會寫很多copypasta手工綁定代碼的,我想tolua ++做這項工作對我來說。我的另一種選擇是在tolua ++的內臟中徘徊,看看我是否可以利用現有的課程代碼。這兩種選擇都不是我覺得愉快的。 – Enno 2011-02-15 18:08:08

回答

1

可以使用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函數返回一個userdata值。 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 

使用用戶數據相同類型共享相同的元表的;因此每個類只應調用一次該函數(假設tolua ++的metatables只構造一次,並且只在退出時釋放)。

在這個答案的底部是一個完整的bar.pkg文件導入bar功能,並增加了一個bar類名爲foo一個Lua模塊。該foo模塊加載到解釋器(see for example my SO tolua++ example)和像這樣使用:

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 
$] 
+0

這是比我想寫的更多的Lua,但它看起來像它將作爲解決方案。非常感謝你長篇大論,寫得很好的答案,明天我下班後,我會盡快回復。 – Enno 2011-02-18 08:45:22