2009-04-07 8 views
6

Ho un oggetto UserData Lua con un determinato tipo di metatable (ad esempio "stackoverflow.test"). Dal codice C, voglio essere in grado di verificare esattamente di che tipo si tratta e comportarsi diversamente a seconda dei risultati. C'è una bella funzione a portata di mano (piuttosto come luaL_checkudata, ma senza errori se la risposta non è quello che vuoi) che mi permetta di interrogare il nome del tipo di metatable del userdata? In caso contrario, suppongo di dover utilizzare lua_getmetatable, ma poi sono un po 'confuso come determino il nome del metatable che è stato appena aggiunto allo stack.Query Lua tipo di dati utente da C

Giusto per chiarire: sto utilizzando Lua 5.1, in cui è stato modificato il comportamento di luaL_checkudata. Capisco che in 5.0 non ha usato per errore.

+0

Sembra che lua 5.2 abbia quello che stai cercando: [luaL_testudata] (http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) –

risposta

3

Userete lua_getmetatable e lua_equal per i test che i tavoli sono gli stessi.

A mio parere, Lua dovrebbe dare più supporto a questo tipo di estensione del testo. A partire da ora, è davvero responsabilità del sistema di wrapper Lua/C (++) farlo.

In un wrapper che ho eseguito di recente (come parte di un progetto commerciale) faccio class::instance(L,index) per ottenere i puntatori di dati utente di un particolare tipo. In altre parole, quel metodo controlla che sia userdata e che anche il metatable sia corretto. In caso contrario, restituisce NULL.

Il modo in cui Lua potrebbe aiutare tutto questo è se la metatable avesse un campo standard per informazioni di tipo esteso (ad esempio __type). Questo potrebbe essere usato in modo che lo stesso type() restituisca "userdata", "xxx" (due valori, che attualmente ne restituiscono uno solo). Questo rimarrebbe compatibile con la maggior parte del codice attuale. Ma questo è solo ipotetico (a meno che tu non faccia un tipo personalizzato() e lo implementi da solo).

0

Ho appena visto il codice sorgente della funzione luaL_checkudata e fondamentalmente recupera la metatable dell'oggetto userdata utilizzando lua_getmetatable. Quindi recupera il nome del tipo specificato dal registro utilizzando lua_getfield e fa una chiamata lua_rawequal per confrontarli.

5

È possibile sempre memorizzare un campo indicatore nel metatable con un valore userdata leggero esclusivo per il modulo.

static const char *green_flavor = "green"; 
... 
void my_setflavor(lua_State *L, void *flavor) { 
    lua_pushlightuserdata(L,flavor); 
    lua_pushlstring(L,"_flavor"); 
    lua_rawset(L,-3); 
} 

void my_isflavor(lua_State *L, void *flavor) { 
    void *p = NULL; 
    lua_pushlstring(L,"_flavor"); 
    lua_rawget(L,-2); 
    p = lua_touserdata(L,-1); 
    lua_pop(L,1); 
    return p == flavor; 
} 

Quindi è possibile utilizzare my_setflavor(L,&green_flavor) per impostare il campo _flavor della tabella in cima alla pila, e my_isflavor(L,&red_flavor) per testare il campo _flavor della tabella in cima alla pila.

Utilizzato in questo modo, il campo _flavor può assumere solo valori che possono essere creati dal codice nel modulo che ha il simbolo green_flavor in ambito, e guardando il campo e verificandone il valore è richiesta solo una ricerca di tabella oltre al recupero della metatable stessa. Si noti che il valore della variabile green_flavor non ha importanza, poiché viene utilizzato solo il suo indirizzo.

Con diverse variabili di sapore distinte disponibili per l'uso come valori sentinella, il campo _flavor può essere utilizzato per distinguere diversi metacollari correlati.

Tutto ciò detto, una domanda naturale è "perché fare questo?" Dopo tutto, la metatable potrebbe facilmente contenere tutte le informazioni necessarie per ottenere il comportamento appropriato. Può contenere prontamente funzioni e dati, e tali funzioni possono essere richiamate e richiamate da C e Lua.

2

Il dato utente deve avere una metatable, quindi afferrarlo; quindi cerca il nome che vuoi nel registro. Se i due oggetti sono identici, hai trovato il tipo che stai cercando.

È possibile inviare questo tipo di codice C, ma consentimi di suggerire gentilmente di designare un campo del metatable. Una funzione memorizzata nel metatable dovrebbe fare il lavoro, ma in caso contrario, se è assolutamente necessario switch in codice C, quindi selezionare un nome, usarlo per indicizzare nel metatable e assegnare a ogni metatable un numero intero piccolo che è possibile attivare.

meta1.decision = 1 
meta2.decision = 2 
meta3.decision = 3 

e poi nel codice C

if (lua_getmetatable(L, 1)) { 
    lua_getfield(L, -1, "decision"); 
    if (lua_isnumber(L, -1)) { 
    switch ((int) lua_tonumber(L, -1)) { 
     case 1: ... ; break; 
     case 2: ... ; break; 
     case 3: ... ; break; 
    } 
    return 0; 
    } 
} 
return luaL_error(L, "Userdata was not one of the expected types");