Luabind: unable to get values ​​from table indexed by non-built classes

I am using luabind 0.9.1 from the main Ryan Pavlik distribution with Lua 5.1, cygwin on Win XP SP3 + latest x86 fixes, boost 1.48, gcc 4.3.4. Lua and boost are precompiled versions of cygwin.

I have successfully created luabind in both static and general versions.

Both versions pass all EXCEPT tests for the test_object_identity.cpp test, which does not work in both versions.

I tracked the problem to the following problem: If a table entry is created for the NON built-in class (i.e. Not int, string, etc.), the value CANNOT be retrieved.

Here is a piece of code that demonstrates this:

#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] is really 10, and tabc [temp_object] is NOT 30! (actually it's nothing)

However, if I use iteration to step through tabc entries, there are two entries with CORRECT key / value pairs.

Any ideas?

BTW, overloading the == operator as follows:

 #include <luabind/operator.hpp> struct test_param { int obj; bool operator==(test_param const& rhs) const { return obj == rhs.obj; } }; 

and

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

Does not change the result.

I also tried switching to settable () and gettable () from the [] operator. The result is the same. I see that the default call to the key is called with the debugger, so I assume that the error occurs somewhere there, but it's outside of me to find out what the problem is.

As shown in the following simple test case, a specific error in Luabind conversion for complex types:

 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; // okv 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 // ok 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 } 

Hope someone smarter than me can figure it out, thanks

I traced the problem that Luabind creates a NEW DISTINCT object EVERY time you use a complex value as a key (but this is NOT if you use a primitive object or an object).

Here is a small test case demonstrating this:

 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); // okv settable( tabc, tp, 5); iterator zzi(tabc), end; std::cerr << "value = " << *zzi << "\n"; zzk = zzi.key(); // okv settable( tabc, tp, 6); settable( tabc, zzk, 7); for (zzi = iterator(tabc); zzi != end; ++zzi) { std::cerr << "value = " << *zzi << "\n"; } } 

Note that tabc [tp] is first set to 5, and then overwritten with 7 when accessed via the key object. However, when accessing AGAIN via tp, a new entry is created. This is why gettable () subsequently fails.

thanks david

+6
source share
1 answer

Disclaimer: I am not a specialist in luabind. Maybe I missed something about the possibilities of luabind.

First of all, what does luabind do when converting test_param to a Lua key? The default policy is copy. To quote the luabind documentation:

This will make a copy of the parameter. This is the default behavior when passing parameters by value. Please note that this can only be used when switching from C ++ to Lua. This policy requires the parameter type to have an available copy constructor.

Basically, this means that luabind will create a new object (called "full userdata") that belongs to the Lua garbage collector and copy your structure into it. This is very safe because it no longer matters what you do with the C ++ object; the Lua object will stand without any overhead. This is a good way to make bindings to sort objects by value.

Why does luabind create a new object every time you pass it to Lua? Well, what else could he do? It doesn't matter if the address of the passed object matches, because the original C ++ object could have been modified or destroyed since it was first passed to Lua. (Remember that it was copied to Lua by value, not by reference.) So, only with ==, luabind would have to list every object of the type that Lua had ever passed (possibly weakly), and Compare your object against each to see if it matches. luabind does not do this (and I don't think it should).

Now let's take a look at the Lua side. Although luabind creates two different objects, they are all the same equal, right? The first problem is that in addition to some built-in types, Lua can only hold objects by reference. Each of these “complete user data” that I mentioned earlier is actually a pointer. This means that they are not identical.

But they are equal if we define the __eq meta-operation. Unfortunately, Lua itself simply does not support this case. User data used as table keys is always compared by identity, no matter what. This really is not special for userdata; this is also true for tables. (Note that in order to properly support this case, Lua needs to override the hashcode operation on the object in addition to __eq. Lua also does not support overriding the hashcode operation.) I cannot speak for Lua authors why they will not allow it (and this was suggested earlier ), but there it is.

So what are the options?

  • The simplest task would be to convert test_param to an object once (explicitly), and then use this object to index the table both times. However, I suspect that although this fixes your toy example, in practice it is not very useful.
  • Another option is to simply not use types such as keys. Actually, I think this is a very good suggestion, since this easier binding is very useful, and the only option is to abandon it.
  • It looks like you can define a custom conversion in your type. In your example, it might be wise to convert your type to a Lua number, which will behave like a table index.
  • Use a different type of binding. There will be some overhead, but if you want to get an identity, you have to live with it. Looks like luabind has some support for wrappers, which you might need to use to preserve your identity:

    When a pointer or reference to a registered wrapper class is passed to Lua, luabind will request a dynamic type for it. If the dynamic type is inherited from wrap_base, the identity of the object is preserved.

0
source

Source: https://habr.com/ru/post/914132/


All Articles