2012-04-25 8 views
6

Estoy usando luabind 0.9.1 de la distribución principal de Ryan Pavlik con Lua 5.1, cygwin en Win XP SP3 + parches más recientes x86, boost 1.48, gcc 4.3.4. Lua y boost son versiones pre compiladas de cygwin.luabind: no se pueden recuperar los valores de la tabla indexada por clases no incorporadas

He creado exitosamente luabind en versiones estáticas y compartidas.

Ambas versiones pasan todas las pruebas EXCEPTO para la prueba test_object_identity.cpp que falla en ambas versiones.

He rastreado el problema hasta el siguiente problema: Si se crea una entrada en una tabla para la clase NON incorporada (es decir, no int, cadena, etc.), el valor NO PUEDE recuperarse.

Aquí es una pieza de código que muestra esto:

#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] es, en efecto, mientras que 10 TABC [temp_object] NO es 30! (en realidad, parece ser nil)

Sin embargo, si utilizo iterar para repasar las entradas de tabc, hay dos entradas con los pares clave/valor CORRECT.

¿Alguna idea?

Por cierto, la sobrecarga del operador == así:

#include <luabind/operator.hpp> 

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

y

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

no cambia el resultado.

También intenté cambiar a settable() y gettable() desde el operador []. El resultado es el mismo. Puedo ver con el depurador que se invoca la conversión predeterminada de la clave, por lo que supongo que el error surge de algún lugar, pero no entiendo cuál es exactamente el problema.

Como el siguiente programa de caso de prueba simple, hay sin duda un error en la conversión de Luabind para este tipo de complejos:

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 
} 

Con suerte, alguien más inteligente que yo puedo resolver esto, Thx

I He rastreado el problema hasta el hecho de que Luabind crea un objeto NUEVO DISTINTO CADA vez que utilizas un valor complejo como clave (pero NO lo hace si usas uno primitivo o un objeto).

Aquí hay un pequeño caso de prueba que lo demuestra:

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"; 
    } 
} 

Observe cómo TABC [tp] primero tiene el valor 5 y luego se sobrescribe con 7 cuando se tiene acceso a través del objeto clave. Sin embargo, cuando se vuelve a acceder a través de tp, se crea una nueva entrada. Es por eso que gettable() falla posteriormente.

Thx, David

+0

¿Alguna vez resolvió este problema? Ya tengo este problema cuando uso valores int como claves para tablas, p. local testTable = {[10] = "verde", [9] = "naranja", [8] = "amarillo"} - si uso cadenas en lugar de números como claves - funciona bien - doy esta tabla como un param a una función de C++ y me sale el error de lanzamiento también – Steve

Respuesta

0

Negación: No soy un experto en luabind. Es completamente posible que me haya perdido algo sobre las capacidades de luabind.

Antes que nada, ¿qué está haciendo luabind al convertir test_param a una clave Lua? La política predeterminada es copiar. Para citar la documentación de luabind:

Esto hará una copia del parámetro. Este es el comportamiento predeterminado al pasar los parámetros por valor. Tenga en cuenta que esto solo se puede usar al pasar de C++ a Lua. Esta política requiere que el tipo de parámetro tenga un constructor de copia accesible.

En pratice, lo que esto significa es que luabind creará un nuevo objeto (llamado "userdata completo"), que es propiedad del recolector de basura Lua y copiará su estructura en ella. Esto es algo muy seguro porque ya no importa lo que haga con el objeto C++; el objeto Lua se quedará sin ningún tipo de sobrecarga. Esta es una buena forma de hacer enlaces para tipos de objetos de valor por valor.

¿Por qué luabind crea un objeto nuevo cada vez que lo pasa a Lua? Bueno, ¿qué más podría hacer? No importa si la dirección del objeto pasado es la misma, porque el objeto C++ original podría haber cambiado o haberse destruido desde que se pasó por primera vez a Lua. (Recuerde, fue copiado a Lua por valor, no por referencia.) Entonces, con solo ==, luabind tendría que mantener una lista de todos los objetos de ese tipo que alguna vez le hayan pasado a Lua (posiblemente débilmente) y comparar su objetar contra cada uno para ver si coincide. luabind no hace esto (ni creo que debería hacerlo).

Ahora, veamos el lado de Lua. A pesar de que luabind crea dos objetos diferentes, siguen siendo iguales, ¿verdad? Bueno, el primer problema es que, además de ciertos tipos incorporados, Lua solo puede retener objetos por referencia. Cada uno de esos "datos de usuario completos" que mencioné antes es en realidad un puntero. Eso significa que no son idénticos.

Pero son iguales, si definimos una operación meta __eq. Desafortunadamente, Lua simplemente no admite este caso. Los datos de usuario cuando se usan como claves de tabla siempre se comparan por identidad, sin importar qué. Esto en realidad no es especial para userdata; también es cierto para las tablas. (Tenga en cuenta que para apoyar adecuadamente este caso, Lua debería anular la operación de código hash en el objeto además de __eq. Lua tampoco admite anular la operación de código hash.) No puedo hablar por los autores de Lua por qué no lo hicieron permitir esto (y se ha sugerido antes), pero ahí está.

Entonces, ¿cuáles son las opciones?

  • Lo más sencillo sería convertir test_param a un objeto una vez (explícitamente), y luego usar ese objeto para indexar la tabla en ambas ocasiones. Sin embargo, sospecho que si bien esto arregla tu ejemplo de juguete, no es muy útil en la práctica.
  • Otra opción es simplemente no usar tipos tales como claves. En realidad, creo que esta es una muy buena sugerencia, ya que este tipo de encuadernación liviana es bastante útil, y la única otra opción es descartarla.
  • Parece que puede definir una conversión personalizada en su tipo. En su ejemplo, podría ser razonable convertir su tipo a un número Lua que se comportará bien como un índice de tabla.
  • Utilice un tipo diferente de encuadernación. Habrá algunos gastos generales, pero si quieres identidad, tendrás que vivir con eso. Suena como luabind tiene algún tipo de apoyo para las envolturas, que puede que tenga que utilizar para preservar la identidad:

    Cuando un puntero o una referencia a una clase certificada con un envoltorio se pasa a Lua, luabind cuestionarán los que es de tipo dinámico . Si el tipo dinámico hereda de wrap_base, se preserva la identidad del objeto.

Cuestiones relacionadas