Anuncios

Bienvenidos sean a este post, para nuestra siguiente tecnica hablaremos de cuando una funcion de C recibe un argumento de tipo cadena desde Lua solo hay dos reglas que se deben observar para no quitar la cadena de la pila mientras se esta accediendo y nunca modificar la cadena.

Anuncios

Las cosas se vuelven mas demandantes cuando una funcion de C necesita crear una cadena para devolver a Lua, ahora va a depender del codigo C hacerse cargo de ubicar/quitar al buffer, sobrecarga del buffer y similares, sin embargo la API de Lua provee algunas funciones para ayudar con estas tareas, la API estandar provee soporte para dos de las operaciones de cadenas mas basicas: extraccion de una subcadena y concatenacion de cadenas, para extraer una subcadena recuerden que la operacion basica lua_pushlstring obtiene la longitud de la cadena como un argumento extra, por lo tanto si quieres pasar a Lua una subcadena de una cadena s en un rango desde la posicion i a la j (inclusive) todo lo que tienes que hacer es:

lua_pushlstring(L, s + i, j - i + 1);
Anuncios

Ahora vamos a suponer que queremos una funcion que separe una cadena de acuerdo al separador informado (un caracter informado) y devuelve una tabla con las subcadenas, por ejemplo la llamada:

split("hey,ho,let's go",",");

Si lo ejecutamos deberia devolvernos una tabla similar a esta:

{ "hey", "ho", "let's go" }
Anuncios

En el siguiente codigo vemos un ejemplo de implementacion de esta funcion:

static int l_split(lua_State *L)
{
	const char *s = luaL_checkstring(L, 1);
	const char *sep = luaL_checkstring(L, 2);
	const char *e;
	int i = 1;

	luaL_newtable(L);

	while((e = strchr(s, *sep)) != NULL)
	{
		lua_pushlstring(L, s, e-s);
		lua_rawseti(L, -2, i++);
		s = e + 1;
	}

	lua_pushlstring(L, s);
	lua_rawseti(L, -2, i);

	return 1;
}
Anuncios

En este codigo no necesitamos buffers extras y no pone restricciones en el tamaño de las cadenas que puede manejar, para concatenar las cadenas Lua provee una funcion especifica en su API llamada lua_concat, es similar al operador .. en Lua ya que convierte numeros a cadenas y dispara metametodos cuando sea necesario, ademas puede concatenar mas que dos cadenas a la vez, la siguiente llamada:

lua_concat(L, n)

Concatenara (y quitara) los n valores en la parte superior de la pila, empujando el resultado a la parte superior, otra funcion util es lua_pushfstring:

const *char lua_pushfstring(lua_State *L, const char *fmt, ...);

Esto es similar la funcion de C sprintf en la que se crea una cadena siguiendo una cadena de formato y algunos argumentos extras, a diferencia de sprintf no necesitas proveer un buffer ya que Lua lo crea dinamicamente por ti tan grande como sea necesario, no habra preocupaciones por la sobrecarga de buffers y similares, la funcion empuja la cadena resultante en la pila y devuelve un apuntador para ello, actualmente esta funcion solo acepta las siguientes directivas:

  • %%, para el caracter %
  • %s, para cadenas
  • %d, para enteros
  • %f, para numeros dobles
  • %c, acepta un entero y lo formatea como un caracter
Anuncios

Y no acepta ninguna opcion como ancho o precision. Tanto lua_concat como lua_pushfstring son utiles cuando queremos concatenar solo unas pocas cadenas, sin embargo si necesitamos concatenar muchas cadenas (o caracteres) juntas, un enfoque uno por uno es muy ineficiente, como vimos en este post, en su lugar podemos usar las facilidades de buffer provistas por la libreria auxiliar, esta funcion implementa estos buffers en dos niveles:

  • el primer nivel es similar a buffers en operaciones I/O (Entrada/Salida), recoge pequeñas cadenas (o caracteres individuales) en un buffer local y los pasa a Lua (con lua_pushlstring) cuando el buffer se llena
  • el segundo nivel usa lua_concat y una variante del algoritmo de la pila, que vimos en este post para concatenar los resultados de multiples descargas del buffer

Para describir las facilidades del buffer de auxlib con mas detalle veremos un simple ejemplo de su uso, el proximo codigo muestra la implementacion de la funcion string.upper, directo del archivo fuente lstrlib.c:

static int str_upper(lua_State *L)
{
	size_t l;
	size_t i;
	luaL_Buffer b;
	const char *s = luaL_checklstr(L, 1, l);
	luaL_buffinit(L, &b);
	for(i = 0; i < l; i++)
		luaL_addchar(&b, toupper((unsigned char)(s[i])));
	luaL_pushresult(&b);
	return 1;
}
Anuncios

El primer paso para usar un buffer de auxlib es declarar una variable con el tipo luaL_Buffer y luego con una llamada inicializar a luaL_Buffinit, despues de la inicializacion el buffer mantiene una copia del estado L, asi que no necesitas pasarlo cuando llamas a otras funciones que manipulan el buffer, la macro luaL_addchar pone un caracter unico dentro del buffer, auxlib tambien ofrece otras funciones para poner dentro del buffer cadenas con una longitud explicita (luaL_addlstring), para finalizar luaL_pushresult descarga el buffer y deja la cadena final en la parte superior de la pila, el prototipo para estas funciones son los siguientes:

void luaL_buffinit	(lua_State *L, luaL_Buffer *B);
void luaL_addchar	(luaL_Buffer *B, char c);
void luaL_addlstring	(luaL_Buffer *B, const char *s, size_t l);
void luaL_addstring	(luaL_Buffer *B, const char *s);
void luaL_pushresult	(luaL_Buffer *B);
Anuncios

Usando estas funciones nos permite no preocuparnos por la sobrecarga de la ubicacion del buffer y otros detalles, ademas como vimos el algoritmo de concatenacion es eficiente, la funcion str_upper maneja cadenas grandes (mas de 1 MB) sin ningun problema.

Anuncios

Cuando usamos el buffer auxlib te tienes que preocupar por un solo detalle, como poner cosas dentro del buffer mientras mantiene algunos resultados intermedios en la pila de Lua, por lo tanto no puedes asumir que la parte superior de la pila se quedara donde estaba antes de haber comenzado a usar un buffer, incluso al construir otro buffer, la cuenta de empujar/quitar para estos usos debe ser balanceado cada vez que accedes al buffer, hay una situacion obvia donde esta restriccion es demasiado severa, a saber cuando quieres poner dentro del buffer una cadena devuelta por Lua, en tales casos no puedes quitar la cadena antes de quitarla de la pila pero tampoco puedes agregar la cadena antes de quitarla porque la pila estaria en el nivel equivocado, como esta es una situacion frecuente auxlib provee una funcion especial para agregar el valor en la parte superior de la pila dentro del buffer:

void luaL_addvalue(luaL_Buffer *B);

Por supuesto que sera un error llamar a la funcion si el valor en la parte superior no es una cadena o un numero.

Anuncios

En resumen, hoy hemos visto como manipular cadenas, hemos hablado de algunas tecnicas que ya hemos visto, hemos visto algunas funciones de la libreria auxiliar, hemos visto como trabajar con otras mas especificas, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00