Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre la tercera de las tecnicas para almacenar estados en funciones de C.

Anuncios

Mientras el registro ofrece variables globales y entornos ofrece variables de modulos, la tecnica upvalue implementa un equivalente de las variables estaticas de C que son visible solo dentro de una funcion particular, cada vez que creamos una nueva funcion de C en Lua podemos asociarlo con cualquier numero de upvalues, cada upvalue puede contener un valor unico de Lua, despues cuando la funcion es llamada tiene libre acceso a cualquiera de sus upvalues usando pseudo-indices, llamamos esta asociacion de una funcion de C con sus upvalues un cierre, un cierre de C es una aproximacion de C a un cierre de Lua, un hecho interesante sobre cierres es que puedes crear diferentes cierres usando el mismo codigo de la funcion pero con diferentes upvalues.

Anuncios

Veamos un ejemplo simple, vamos a crear una funcion llamada nuevoContador en C, la cual hicimos para Lua en este post, la cual es una fabrica y este devuelve una nueva funcion de contador cada vez que es llamada aunque todos los contadores compartan el mismo codigo de C y cada una mantiene su propio e independiente contador, la funcion de fabrica es como el siguiente codigo:

static int countador(lua_State *L);

int nuevoContador(lua_State *L)
{
	lua_pushinteger(L, 0);
	lua_pushcclosure(L, &contador, 1);
	return 1;
}
Anuncios
Anuncios

La funcion clave aca es lua_pushcclosure la cual crea un nuevo cierre, su segundo argumento es la funcion base (contador en el ejemplo), y el tercero es el numero de upvalues (1 en el ejemplo), antes de crear un nuevo cierre debemos empujar en la pila los valores iniciales para sus upvalues, en nuestro ejemplo empujamos el valor 0 como el valor inicial para sus upvalues, como se espera lua_puscclosure deja un nuevo cierre en la pila, asi el cierre esta listo para ser devuelto como el resultado de nuevoContador, pasemos a ver la definicion de contador:

static int countador(lua_State *L)
{
	int val = lua_tointeger(L, lua_upvalueindex(1));
	lua_pushinteger(L, ++val);
	lua_pushvalue(L, -1);
	lua_replace(L, lua_upvalueindex(1));
	return 1;
}
Anuncios

En este codigo la funcion clave es lua_upvalueindex (la cual es una macro), esta produce el pseudo-indice de un upvalue, como en otras ocasiones mencionamos este pseudo-indice es como cualquier indice de pila, excepto que este no vive en la pila, la expresion lua_upvalueindex(1) se refiere al primer valor de upvalue de la funcion asi que la llamada a lua_tointeger recupera el valor actual del primer (y solo) upvalue como un numero, luego la funcion contador empuja el nuevo valor ++val, hace una copia de este y usa una de estas copias para reemplazar el valor de upvalue, para finalmente devolver la otra copia como su valor de retorno.

Como un ejemplo mas avanzado implementaremos tuplas usando upvalues, una tupla es una especie de registro constante con campos anonimos, puedes recuperar un campo especifico con un numero de indice o puedes recuperar todos los campos de una vez, en nuestra implementacion representamos tuplas como funciones que almacenan sus valores en sus upvalues, cuando es llamado con un argumento numerico la funcion devuelve el campo especificado, en cambio cuando lo llamamos sin argumento nos devuelve todos sus campos, el siguiente codigo ilustra el uso de tuplas:

x = tuple.new(10, "hola", {}, 3)

print(x(1))	-- Devuelve 10
print(x(2))	-- Devuelve hola
print(x())	-- Devuelve 10	hola	table: 0x8087878	3
Anuncios

En C representamos todas las tuplas con la misma funcion t_tupla como se ve en el siguiente codigo:

int t_tupla(lua_State *L)
{
	int op = luaL_opint(L, 1, 0);
	if (op == 0)
	{
		int i;
		for(i = 1; !lua_isnone(L, lua_upvalueindex(i)); i++)
			lua_pushvalue(L, lua_upvalueindex(i));
		return i - 1;
	} else {
		luaL_argcheck(L, 0 < op, 1, "indice fuera del rango");
		if (lua_isnone(L, lua_upvalueindex(op)))
			return 0:
		lua_pushvalue(L, lua_upvaluindex(op));
		return 1;
	}
}

int t_nueva(lua_State *L)
{
	lua_pushcclosure(L, t_tupla, lua_gettop(L));
	return 1;
}

static const struct luaL_Reg tuplalib [] = {
	{"nuevo", t_nueva},
	{NULL, NULL}
}

int luaopen_tupla(lua_State *L)
{
	luaL_register(L, "tupla", tuplalib);
	return 1;
}
Anuncios

Debido a que podemos llamar a tuple con o sin argumentos numericos t_tuple usa luaL_optint para conseguir su argumento opcional, la funcion luaL_optint trabaja de forma similar a luaL_checkint pero no contempla si el argumento esta ausente, en su lugar devuelve un valor predeterminado seteado por nosotros, en este caso 0, cuando indexamos un upvalue inexistente el resultado es un pseudo-valor cuyo tipo es LUA_TNONE, cuando accedemos a un indice de la parte superior actual de la pila tambien conseguimos un pseudo-valor de tipo LUA_TNONE, asi que nuestra funcion t_tupla usa lua_isnone para chequear si tiene un upvalue informado, sin embargo no deberias llamar nunca a lua_upvalueindex con un indice negativo, por esta razon deberas chequear por esta condicion cuando el usuario provea el indice, la funcion luaL_argcheck chequea la condicion informada devolviendo un error si es necesario.

Anuncios

La funcion para crear tuplas, t_nueva es trivial porque sus argumentos ya estan en la pila, solo tienes que llamar a lua_pushcclosure para crear un cierre de t_tupla con estos argumentos como upvalues, por el ultimo el array tuplalib y la funcion luaopen_tupla son el codigo estandar para crear una libreria tupla con la funcion nueva.

Anuncios

En resumen, hoy hemos visto la ultima tecnica que podemos usar para almacenar estados en funciones de C, hemos visto que nos provee, como se puede usar, sus tecnicas para usarlas, un ejemplo practico para implementarla, 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