2012-04-25 49 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继承,则保留对象标识。