luabind:无法从非内置类索引的表中检索值

时间:2012-04-25 13:23:26

标签: c++ lua luabind

我正在使用Luan Pavlik的Luabind 0.9.1与Lua 5.1的主发行版,在Win XP SP3上的cygwin +最新补丁x86,增强1.48,gcc 4.3.4。 Lua和boost是cygwin预编译的版本。

我已经在静态和共享版本中成功构建了luabind。

两个版本都通过了test_object_identity.cpp测试的所有测试EXCEPT,这两个版本都失败了。

我已将问题追溯到以下问题: 如果为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! (实际上,它似乎是零)

但是,如果我使用iterate来查看tabc条目,则有两个条目具有CORRECT键/值对。

有什么想法吗?

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 
} 

希望比我更聪明的人可以解决这个问题, 谢谢

我已经将问题追溯到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访问AGAIN时,会创建一个新条目。这就是gettable()随后失败的原因。

THX, 大卫

1 个答案:

答案 0 :(得分:0)

免责声明:我不是luabind的专家。我完全有可能错过了关于luabind的能力。

首先,在将test_param转换为Lua密钥时luabind在做什么?默认策略是copy。引用luabind文档:

  

这将生成参数的副本。这是按值传递参数时的默认行为。请注意,这只能在从C ++传递到Lua时使用。此策略要求参数类型具有可访问的复制构造函数。

在实践中,这意味着luabind将创建一个新对象(称为&#34;完整用户数据&#34;),它由Lua垃圾收集器拥有,并将您的结构复制到其中。这是一件非常安全的事情,因为你对c ++对象的处理方式不再重要; Lua对象会在没有任何开销的情况下坚持下去。这是对按值的对象进行绑定的好方法。

为什么luabind每次将它传递给Lua时都会创建一个新对象?那么,它还能做些什么呢?如果传递的对象的地址相同则无关紧要,因为原始的c ++对象可能已经更改或被破坏,因为它首次传递给Lua。 (请记住,它是通过值复制到Lua,而不是通过引用复制。)因此,只有==,luabind必须维护一个已经传递给Lua(可能是弱的)的那种类型的每个对象的列表并比较你的反对每一个以查看它是否匹配。 luabind没有做到这一点(我认为也不应该这样做)。

现在,让我们来看看Lua方面。即使luabind创造了两个不同的物体,它们仍然相等,对吧?嗯,第一个问题是,除了某些内置类型之外,Lua只能通过引用来保存对象。每个&#34;完整的用户数据&#34;我之前提到的实际上是一个指针。这意味着它们并不相同。

但是如果我们定义一个__eq元操作,它们是相同的。不幸的是,Lua本身并不支持这种情况。用作表键的用户数据总是按身份进行比较,无论如何。这对于userdata来说实际上并不特殊;表格也是如此。 (注意,为了正确支持这种情况,除了__eq之外,Lua还需要覆盖对象的哈希码操作.Lua也不支持覆盖哈希码操作。)我不能代表Lua的作者为什么他们说话不允许这样做(之前已经提出过),但确实如此。

那么,有哪些选择?

  • 最简单的方法是将test_param转换为对象一次(显式),然后使用该对象将表索引两次。但是,我怀疑虽然这会修复你的玩具示例,但在实践中它并没有多大帮助。
  • 另一个选择就是不要使用键这样的类型。实际上,我认为这是一个非常好的建议,因为这种轻量级绑定非常有用,唯一的另一种选择就是丢弃它。
  • 您似乎可以在类型上定义自定义转化。在您的示例中,将您的类型转换为Lua编号可能是合理的,这将作为表索引。
  • 使用其他类型的绑定。会有一些开销,但如果你想要身份,你就必须忍受它。听起来luabind对包装器有一些支持,您可能需要使用它来保护标识:

      

    当带有包装器的已注册类的指针或引用传递给Lua时,luabind将查询它的动态类型。如果动态类型继承自wrap_base,则保留对象标识。

  •