2009-04-07 9 views
6

J'ai un objet Lua userdata avec un certain type metatable (par exemple "stackoverflow.test"). À partir du code C, je veux être capable de vérifier exactement de quel type il s'agit et de me comporter différemment selon les résultats. Existe-t-il une bonne fonction pratique (plutôt comme luaL_checkudata, mais sans erreur si la réponse n'est pas ce que vous voulez) qui permettez-moi d'interroger le nom de type metatable de la userdata? Sinon, je suppose que j'ai besoin d'utiliser lua_getmetatable, mais je ne sais pas comment déterminer le nom de la méta-donnée qui vient d'être ajoutée à la pile. Juste pour clarifier: j'utilise Lua 5.1, où le comportement de luaL_checkudata a été changé. Je comprends que dans 5.0, il n'a pas l'habitude d'erreur.Demande le type Lua userdata à partir de C

+0

Il semble que lua 5.2 ait ce que vous cherchez: [luaL_testudata] (http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) –

Répondre

3

Vous allez utiliser lua_getmetatable et lua_equal pour les tests que les tables sont les mêmes. À mon avis, Lua devrait donner plus de soutien à ce genre d'extension de type. A partir de maintenant, c'est vraiment la responsabilité du système d'encapsulation Lua/C (++) de le faire.

Dans un emballage que j'ai fait récemment (dans le cadre d'un projet commercial), je fais class::instance(L,index) pour obtenir des pointeurs userdata d'un type particulier. En d'autres termes, cette méthode vérifie que c'est userdata et que la métatable est également correcte. Sinon, il renvoie NULL.

La façon dont Lua pourrait aider tout ceci est que la métatable ait un champ standard pour les informations de type étendu (c'est-à-dire __type). Cela pourrait être utilisé de sorte que type() lui-même retournerait "userdata", "xxx" (deux valeurs, n'en renvoyant actuellement qu'une seule). Cela resterait compatible avec la plupart du code actuel. Mais c'est juste hypothétique (à moins que vous ne fassiez un type personnalisé() et l'implémentez vous-même).

0

Je viens de regarder le code source à la fonction luaL_checkudata, et il récupère fondamentalement la métatable de l'objet userdata en utilisant lua_getmetatable. Il extrait ensuite le nom de type donné du registre à l'aide de lua_getfield et effectue un appel lua_rawequal pour les comparer.

5

Vous pouvez toujours stocker un champ de marqueur dans la métatable avec une valeur de données utilisateur légère unique à votre module.

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

Ensuite, vous pouvez utiliser my_setflavor(L,&green_flavor) pour définir le champ de _flavor de la table en haut de la pile, et my_isflavor(L,&red_flavor) pour tester le champ _flavor de la table en haut de la pile. Utilisé de cette façon, le champ _flavor ne peut prendre que les valeurs qui peuvent être créées par le code dans le module qui a le symbole green_flavor dans la portée, et rechercher le champ et tester sa valeur requiert uniquement une recherche de table en dehors de la récupération de la métatable elle-même. Notez que la valeur de la variable green_flavor n'a pas d'importance, puisque seule son adresse est réellement utilisée.

Avec plusieurs variables de saveur distinctes disponibles à utiliser comme valeurs sentinelles, le champ _flavor peut être utilisé pour distinguer plusieurs métatables associées.

Tout cela dit, une question naturelle est "pourquoi faire ceci du tout?" Après tout, le metatable pourrait facilement contenir toutes les informations dont il a besoin pour obtenir le comportement approprié. Il peut facilement contenir des fonctions ainsi que des données, et ces fonctions peuvent être récupérées et appelées depuis C et Lua.

2

La variable userdata doit avoir un metatable, donc saisissez-la; puis recherchez le nom que vous voulez dans le registre. Si les deux objets sont identiques, vous avez trouvé le type que vous recherchez.

Vous pouvez envoyer sur ce type en code C, mais permettez-moi de suggérer que vous désigniez un champ de la métatable à la place. Une fonction stockée dans la métatable devrait faire le travail, mais si ce n'est pas le cas, si vous devez absolument switch en C, choisissez un nom, utilisez-le pour indexer dans la métatable, et assignez à chaque métatable un petit entier que vous pouvez activer.

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

puis dans votre code 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"); 
Questions connexes