2012-04-25 71 views
6

我使用Ryan Pavlik的Lua 5.1的主分佈的luabind 0.9.1,Win XP SP3上的cygwin +最新的補丁x86,boost 1.48,gcc 4.3.4。 Lua和boost是cygwin預編譯版本。luabind:無法從非內置類索引表中檢索值

我已經在靜態和共享版本中成功構建了luabind。

兩個版本都通過了test_object_identity.cpp測試的所有測試,其中兩個版本均失敗。

我追查到以下問題: 如果爲NON內置類(即不是int,string等)創建表中的條目,則無法檢索值。

這裏有一個代碼塊演示了此:

#include "test.hpp" 
#include <luabind/luabind.hpp> 
#include <luabind/detail/debug.hpp> 

using namespace luabind; 

struct test_param 
{ 
    int obj; 
}; 

void test_main(lua_State* L) 
{ 
    using namespace luabind; 

    module(L) 
    [ 
     class_<test_param>("test_param") 
      .def_readwrite("obj", &test_param::obj) 
    ]; 

    test_param temp_object; 
    object tabc = newtable(L); 
    tabc[1] = 10; 
    tabc[temp_object] = 30; 

    TEST_CHECK(tabc[1] == 10);    // passes 
    TEST_CHECK(tabc[temp_object] == 30); // FAILS!!! 

} 

TABC [1]的確是10,而TABC [temp_object]不是30! (實際上,它似乎是零)

但是,如果我使用迭代來檢查tabc條目,那麼有兩個條目與正確的鍵/值對。

任何想法?

BTW,重載==操作符是這樣的:

#include <luabind/operator.hpp> 

struct test_param 
{ 
    int obj; 
    bool operator==(test_param const& rhs) const 
    { 
     return obj == rhs.obj; 
    } 
}; 

module(L) 
    [ 
     class_<test_param>("test_param") 
      .def_readwrite("obj", &test_param::obj) 
      .def(const_self == const_self) 
    ]; 

不改變結果。

我也嘗試從[]運算符切換到settable()和gettable()。結果是一樣的。我可以用調試器看到調用了默認的密鑰轉換,所以我想這個錯誤來自其中的某個地方,但是我無法弄清楚究竟是什麼問題。

如下面的簡單的測試案例顯示,有是肯定的Luabind的轉換爲複雜類型的錯誤:

struct test_param : wrap_base 
{ 
    int obj; 
    bool operator==(test_param const& rhs) const 
    { return obj == rhs.obj ; } 
}; 

void test_main(lua_State* L) 
{ 
    using namespace luabind; 
    module(L) 
    [ 
     class_<test_param>("test_param") 
       .def(constructor<>()) 
       .def_readwrite("obj", &test_param::obj) 
       .def(const_self == const_self) 
    ]; 

    object tabc, zzk, zzv; 
    test_param tp, tp1; 
    tp.obj = 123456; 
    // create new table 
    tabc = newtable(L); 
    // set tabc[tp] = 5; 
    //   o  k v 
    settable(tabc, tp, 5); 
    // get access to entry through iterator() API 
    iterator zzi(tabc); 
    // get the key object 
    zzk = zzi.key(); 
    // read back the value through gettable() API 
    //    o  k 
    zzv = gettable(tabc, zzk); 
    // check the entry has the same value 
    // irrespective of access method 
    TEST_CHECK (*zzi == 5 && 
       object_cast<int>(zzv) == 5); 
    // convert key to its REAL type (test_param) 
    tp1 = object_cast<test_param>(zzk); 
    // check two keys are the same 
    TEST_CHECK(tp == tp1); 
    // read the value back from table using REAL key type 
    zzv = gettable(tabc, tp1); 
    // check the value 
    TEST_CHECK(object_cast<int>(zzv) == 5); 
    // the previous call FAILS with 
    // Terminated with exception: "unable to make cast" 
    // this is because gettable() doesn't return 
    // a TRUE value, but nil instead 
} 

希望有人比我聰明可以算出來, THX

我已經將問題追溯到Luabind在每次使用複雜值作爲關鍵字時創建一個NEW DISTINCT對象這​​一事實(但如果您使用原始對象或對象,則不會這樣做)。

這裏的演示此小測試用例:

struct test_param : wrap_base 
{ 
    int obj; 
    bool operator==(test_param const& rhs) const 
    { return obj == rhs.obj ; } 
}; 

void test_main(lua_State* L) 
{ 
    using namespace luabind; 

    module(L) 
    [ 
     class_<test_param>("test_param") 
      .def(constructor<>()) 
      .def_readwrite("obj", &test_param::obj) 
      .def(const_self == const_self) 
    ]; 

    object tabc, zzk, zzv; 
    test_param tp; 
    tp.obj = 123456; 
    tabc = newtable(L); 
    //   o  k v 
    settable(tabc, tp, 5); 
    iterator zzi(tabc), end; 
    std::cerr << "value = " << *zzi << "\n"; 
    zzk = zzi.key(); 
    //   o  k v 
    settable(tabc, tp, 6); 
    settable(tabc, zzk, 7); 
    for (zzi = iterator(tabc); zzi != end; ++zzi) 
    { 
     std::cerr << "value = " << *zzi << "\n"; 
    } 
} 

通知如何TABC [TP]第一具有值5,然後通過按鍵對象訪問時改寫爲7。但是,當通過tp重新訪問時,會創建一個新條目。這就是gettable()隨後失敗的原因。

THX, 大衛

+0

您是否曾經解決過這個問題?使用int值作爲表格的鍵時,我已經遇到了這個問題。 local testTable = {[10] =「green」,[9] =「orange」,[8] =「yellow」} - 如果我使用字符串而不是數字作爲鍵 - 它工作正常 - 我把這個表作爲參數到一個C++函數,我也得到了鑄造錯誤 – Steve 2012-07-08 22:22:02

回答

0

免責聲明:我不是luabind的專家。我完全有可能錯過了關於luabind的能力。

首先,在將test_param轉換爲Lua密鑰時,luabind在做什麼?默認策略是複製。引用luabind文檔:

這將製作參數的副本。這是按值傳遞參數時的默認行爲。請注意,這隻能在從C++傳遞到Lua時使用。此策略要求參數類型具有可訪問的拷貝構造函數。

實際上,這意味着luabind將創建一個由Lua垃圾回收器擁有的新對象(稱爲「完整用戶數據」),並將複製您的結構體到其中。這是一件非常安全的事情,因爲它不再關心你用C++對象做什麼; Lua對象將繼續存在而沒有任何開銷。這是爲按值對象進行綁定的好方法。

爲什麼luabind每次將它傳遞給Lua時都會創建一個新對象?那麼,它還能做什麼?傳遞對象的地址是否相同並不重要,因爲原來的C++對象可能在第一次傳遞給Lua後發生了更改或被銷燬。 (請記住,它是通過值複製到Lua的,而不是通過引用)。因此,只有==,luabind將不得不維護一個已經傳遞給Lua(可能很弱)的該類型的每個對象的列表,並比較你的反對每一個對象來查看它是否匹配。 luabind不會這樣做(我認爲不應該)。

現在,讓我們來看看Lua方面。即使luabind創建兩個不同的對象,它們仍然是平等的,對吧?那麼,第一個問題是,除了某些內置類型,Lua只能通過引用來保存對象。我之前提到的那些「完整用戶數據」其實都是一個指針。這意味着它們不相同。

但是他們是平等的,如果我們定義一個__eq元操作。不幸的是,Lua本身並不支持這種情況。用作表格鍵時的用戶數據總是通過身份進行比較,無論如何。這對userdata實際並不特別;表格也是如此。 (請注意,爲了正確支持這種情況,除了__eq之外,Lua還需要重寫對象的哈希碼操作,Lua也不支持重寫哈希碼操作。)我不能爲Lua的作者說出爲什麼他們沒有允許這個(之前已經提出過),但是它就是這樣。

那麼,有什麼選擇?

  • 最簡單的事情將是test_param轉換爲對象一次(明確的),然後使用該對象來索引的表兩次。不過,我懷疑這雖然修復了你的玩具例子,但它在實踐中並不是很有幫助。
  • 另一種選擇是根本不使用鍵等類型。其實,我認爲這是一個非常好的建議,因爲這種輕量級綁定非常有用,唯一的選擇就是放棄它。
  • 它看起來像你可以定義你的類型的自定義轉換。在您的示例中,將您的類型轉換爲表格索引表現良好的Lua編號可能是合理的。
  • 使用不同種類的綁定。會有一些開銷,但如果你想要身份,你必須忍受它。這聽起來像luabind有包裝,您可能需要使用保存的身份一定的支持:

    當指針或引用的註冊類包裝傳遞到Lua,luabind將查詢它的動態類型。如果動態類型從wrap_base繼承,則保留對象標識。