2009-04-07 9 views
6

Tengo un objeto de usuario de Lua con un cierto tipo de metatabla (por ejemplo, "stackoverflow.test"). Desde el código C, deseo poder verificar exactamente qué tipo es y comportarme de manera diferente según los resultados. ¿Existe una buena función útil (más bien como luaL_checkudata, pero sin error si la respuesta no es la que desea) que nos permite consultar el nombre de tipo metatabla de los datos de usuario? Si no, supongo que necesito usar lua_getmetatable, pero no estoy muy claro cómo determiné el nombre del metatabla que acaba de agregarse a la pila.Tipo de datos de usuario de Query Lua de C

Solo para aclarar: Estoy usando Lua 5.1, donde se modificó el comportamiento de luaL_checkudata. Entiendo que en 5.0 no estaba acostumbrado al error.

+0

Parece que lua 5.2 tiene lo que estás buscando: [luaL_testudata] (http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) –

Respuesta

3

Vamos a usar lua_getmetatable y lua_equal hace para comprobar que las tablas son los mismos.

En mi opinión, Lua debería dar más soporte a este tipo de cosas que se extienden. A partir de ahora, es realmente responsabilidad del sistema de envoltura Lua/C (++) hacer eso.

En un contenedor que he hecho recientemente (como parte de un proyecto comercial) hago class::instance(L,index) para obtener punteros userdata de un tipo en particular. En otras palabras, ese método verifica que sean datos de usuario y que la metatabla también sea correcta. Si no, devuelve NULL.

La forma en que Lua podría ayudar con todo esto es si el metatabla tenía un campo estándar para la información de tipo extendido (por ejemplo, __type). Esto se podría usar para que type() devolviera "userdata", "xxx" (dos valores, actualmente solo devuelve uno). Esto seguiría siendo compatible con la mayoría del código actual. Pero esto es solo hipotético (a menos que haga un tipo personalizado() e implemente esto por su cuenta).

0

Acabo de ver el código fuente de la función luaL_checkudata, y básicamente recupera el metatabulario del objeto userdata usando lua_getmetatable. A continuación, recupera el nombre de tipo proporcionado del registro utilizando lua_getfield y realiza una llamada lua_rawequal para compararlos.

5

Siempre puede almacenar un campo de marcador en la metatabla con un valor de usuario ligero que sea exclusivo de su módulo.

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

continuación, puede utilizar my_setflavor(L,&green_flavor) para establecer el campo _flavor de la tabla en la parte superior de la pila, y my_isflavor(L,&red_flavor) para probar el campo _flavor de la tabla en la parte superior de la pila.

Utilizado de esta manera, el campo _flavor solo puede tomar valores que pueden crearse por código en el módulo que tiene el símbolo green_flavor en el alcance, y buscar el campo y probar su valor solo requiere una búsqueda de tablas además de la recuperación del metatabla en sí. Tenga en cuenta que el valor de la variable green_flavor no importa, ya que solo se usa su dirección.

Con varias variables de sabor distintas disponibles para usar como valores de sentido, el campo _flavor se puede usar para distinguir varias metatatas relacionadas.

Dicho todo esto, una pregunta natural es "¿por qué hacer esto en absoluto?" Después de todo, el metatable podría contener fácilmente toda la información que necesita para obtener el comportamiento apropiado. Puede contener fácilmente funciones y datos, y esas funciones se pueden recuperar y llamar desde C y Lua.

2

La userdata tiene que tener un metatable, así que agarra eso; luego busca el nombre que deseas en el registro. Si los dos objetos son idénticos, has encontrado el tipo que estás buscando.

Puede enviar este tipo de código C, pero permítame sugerirle que designe un campo del metatabla. Una función almacenada en la metatabla debería hacer el trabajo, pero si no, si tiene absolutamente que switch en código C, elija un nombre, utilícelo para indexar en la metatabla y asigne a cada metatabla un entero pequeño que pueda activar.

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

y luego en el código 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");