2012-04-25 7 views
6

Uso luabind 0.9.1 dalla distribuzione principale di Ryan Pavlik con Lua 5.1, cygwin su Win XP SP3 + ultime patch x86, boost 1.48, gcc 4.3.4. Lua e boost sono versioni precompilate cygwin.luabind: impossibile recuperare i valori dalla tabella indicizzata dalle classi non integrate

Ho sviluppato con successo luabind in entrambe le versioni statiche e condivise.

Entrambe le versioni superano tutti i test ECCETTO per il test test_object_identity.cpp che non riesce in entrambe le versioni.

Ho rintracciato il problema al seguente problema: Se una voce in una tabella viene creata per la classe NON integrata (vale a dire, non int, stringa, ecc.), Il valore NON può essere recuperato.

Ecco un pezzo di codice che illustra questo:

#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] è infatti 10, mentre TABC [temp_object] NON è 30! (in realtà, sembra essere nullo)

Tuttavia, se uso iterate per andare oltre le voci di tabc, ci sono le due voci con le coppie chiave/valore CORRETTO.

Qualche idea?

BTW, sovraccaricare l'operatore == in questo modo:

#include <luabind/operator.hpp> 

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

e

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

non cambia il risultato.

Ho anche provato a passare a settable() e gettable() dall'operatore []. Il risultato è lo stesso. Riesco a vedere con il debugger che viene invocata la conversione predefinita della chiave, quindi suppongo che l'errore derivi da qualche parte in esso, ma è al di là di me capire quale sia esattamente il problema.

Come il seguente semplice caso di test spettacolo, ci sei sicuramente un errore nella conversione di Luabind per i tipi complessi:

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 
} 

Si spera, qualcuno più intelligente di me riesco a capire questo, Thx

I Ho tracciato il problema del fatto che Luabind crea un NUOVO oggetto DISTINCT OGNI volta che usi un valore complesso come chiave (ma NON se usi uno primitivo o un oggetto).

Ecco un piccolo banco di prova che illustra questo:

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

Notate come TABC [TP] ha prima il valore 5 e poi viene sovrascritto con 7 quando si accede tramite l'oggetto chiave. Tuttavia, quando si accede AGAIN tramite tp, viene creata una nuova voce. Questo è il motivo per cui gettable() fallisce successivamente.

Thx, David

+0

hai mai risolto questo problema? Ho già questo problema quando utilizzo i valori int come chiavi per le tabelle, ad es. testTable locale = {[10] = "green", [9] = "orange", [8] = "yellow"} - se uso stringhe invece di numeri come chiavi - funziona bene - fornisco questa tabella come parametro a una funzione di C++ e ottengo anche l'errore di cast – Steve

risposta

0

Disclaimer: io non sono un esperto di luabind. È del tutto possibile che mi sia sfuggito qualcosa delle capacità di luabind.

Prima di tutto, cosa sta facendo luabind quando converte test_param in una chiave Lua? La politica predefinita è copia. Per citare la documentazione di luabind:

Ciò farà una copia del parametro. Questo è il comportamento predefinito durante il passaggio dei parametri in base al valore. Nota che questo può essere usato solo quando passi da C++ a Lua. Questa politica richiede che il tipo di parametro abbia un costruttore di copia accessibile.

In pratica, ciò significa che luabind creerà un nuovo oggetto (chiamato "userdata pieno"), che è di proprietà del garbage collector Lua e copierà la vostra struct in esso. Questa è una cosa molto sicura da fare perché non conta più cosa si fa con l'oggetto C++; l'oggetto Lua rimarrà in giro senza alcun sovraccarico. Questo è un buon modo per fare associazioni per oggetti di valore per valore.

Perché luabind crea un nuovo oggetto ogni volta che lo passi a Lua? Bene, cos'altro potrebbe fare? Non importa se l'indirizzo dell'oggetto passato è lo stesso, perché l'oggetto C++ originale avrebbe potuto essere cambiato o distrutto da quando è stato passato per la prima volta a Lua. (Ricorda, è stato copiato su Lua in base al valore, non in base al riferimento.) Quindi, con solo ==, luabind dovrebbe mantenere un elenco di ogni oggetto di quel tipo che è mai stato passato a Lua (possibilmente debolmente) e confrontare il tuo oggetto contro ciascuno per vedere se corrisponde. luabind non lo fa (né penso che dovrebbe farlo).

Ora, diamo un'occhiata al lato Lua. Anche se luabind crea due oggetti diversi, sono uguali, giusto? Bene, il primo problema è che, oltre a certi tipi built-in, Lua può solo contenere oggetti per riferimento. Ognuno di questi "full userdata" che ho citato prima è in realtà un puntatore. Ciò significa che non sono identici.

Ma sono uguali se definiamo un'operazione metrica __eq. Sfortunatamente, la stessa Lua semplicemente non supporta questo caso. I dati utente utilizzati come chiavi di tabella vengono sempre confrontati in base all'identità, a prescindere da cosa. Questo in realtà non è speciale per i dati utente; è anche vero per le tabelle. (Si noti che per supportare correttamente questo caso, Lua avrebbe bisogno di sovrascrivere l'operazione hashcode sull'oggetto oltre a __eq. Anche Lua non supporta l'override dell'operazione hashcode.) Non posso parlare per gli autori di Lua perché non l'hanno fatto permetti questo (ed è stato suggerito prima), ma eccolo.

Quindi, quali sono le opzioni?

  • La cosa più semplice sarebbe quello di convertire test_param ad un oggetto una volta (esplicitamente), e quindi utilizzare tale oggetto per indicizzare il tavolo entrambe le volte. Tuttavia, ho il sospetto che mentre questo risolva il tuo esempio di giocattolo, non è molto utile nella pratica.
  • Un'altra opzione è semplicemente non utilizzare tipi come chiavi. In realtà, penso che questo sia un ottimo suggerimento, dal momento che questo tipo di rilegatura leggera è abbastanza utile, e l'unica altra opzione è scartarla.
  • Sembra che tu possa definire una conversione personalizzata sul tuo tipo. Nel tuo esempio, potrebbe essere ragionevole convertire il tuo tipo in un numero Lua che si comporterà bene come indice di una tabella.
  • Utilizzare un tipo diverso di rilegatura. Ci sarà un sovraccarico, ma se vuoi identità, dovrai conviverci. Sembra che luabind ha qualche supporto per i wrapper, che potrebbe essere necessario utilizzare per preservare l'identità:

    Quando un puntatore o un riferimento a una classe raccomandata con un wrapper viene passato a Lua, luabind potranno interrogare per il suo tipo dinamico . Se il tipo dinamico eredita da wrap_base, l'identità dell'oggetto viene preservata.