Librer铆as de ayuda a la generaci贸n del glue code

Este art铆culo completa el que publiqu茅 la semana pasada y que puede acceder haciendo click aqu铆.

Librer铆as de ayuda a la generaci贸n del glue code

Por lo general lo m谩s dificultoso, en la integraci贸n de un motor de scripts a un motor de juegos, pasa por la exportaci贸n del modelo de objetos para que el mismo sea visible desde el script. Este c贸digo, denominado “glue code”, generalmente est谩 relacionado a la exportaci贸n de funciones y mapeo de tipos de datos.

De esta manera, si poseo un personaje y quiero que el mismo se traslade de un posici贸n A a una posici贸n B deber茅 poder dar estas instrucciones desde Python (o el lenguaje de script que corresponda) para que esto suceda, adem谩s desde el script deber茅 poder consultar o recibir informaci贸n acerca de la interacci贸n de un determinado objeto o personaje con el mundo (ej: colisiones).

Existen diversas soluciones para generar este c贸digo de manera “autom谩tica” o, mejor dicho, de una manera menos manual. Veamos algunas de ellas:

. Para Lua podemos utilizar la herramienta/librer铆a ToLua (http://www.tecgraf.puc-rio.br/~celes/tolua/tolua-3.2.html).

. Para Python (y otros lenguajes de scripts) podemos utilizar la librer铆a Swig (Simplified Wrapper and Interface Generator) (http://www.swig.org).

ToLua

ToLua es una herramienta que podremos descargar de modo libre desde http://www.tecgraf.puc-rio.br/~celes/tolua/

Haciendo uso de esta herramienta, la cantidad de c贸digo que deberemos escribir para integrar scripts Lua con nuestros programas ser谩 mucho menor.

La herramienta se encuentra compuesta por dos componentes:

. Un precompilador (una utilidad que lee un archivo de nuestro proyecto y genera c贸digo en lenguaje C++)
. Una librer铆a que debe ser incorporada a nuestro proyecto.

Vayamos al ejemplo de acceso a una clase de nuestra librer铆a (que es el caso m谩s com煤n en nuestros programas C++). Supongamos que tenemos la siguiente clase:

Archivo: ClaseFoo.h

| class ClaseFoo
| {
| public:
| ClaseFoo();
| ~ClaseFoo();
|
| int m_valor;
|
| void SetValor(int iVal);
| int GetValor();
| };

Entonces, deberemos crear otro archivo llamado, por ejemplo, ClaseFoo.pkg que ser谩 un archivo cabecera pero simplificado y donde no se incluir谩n los m茅todos o propiedades privadas o protegidas que deseamos esconder de Lua, veamos:

Archivo: ClaseFoo.pkg


| #include "ClaseFoo.h"
| class ClaseFoo
| {
| public:
| ClaseFoo();
| ~ClaseFoo();
| int m_valor;
|
| void SetValor(int iVal);
| int GetValor();
| };

Como se puede apreciar la sintaxis es casi la misma del archivo cabecera. Notemos que existe un include colocado con un $ delante, esto es para que la herramienta ToLua no lo interprete y simplemente lo transfiera al archivo C++ generado (esto ser谩 necesario, pues ClaseFoo hace referencia a la clase declarada en ClaseFoo.h).

Ahora, este archivo lo procesaremos con la herramienta:
tolua -o clasefoo.cxx clasefoo.pkg

La herramienta tomar谩 como entrada al archivo clasefoo.pkg y crear谩 la salida con el nombre de clasefoo.cxx (que es una terminaci贸n v谩lida de un archivo C++).

Extracto del archivo generado:


|
| /*
| ** Lua binding: clasefoo
| ** Generated automatically by tolua 5.0a on 11/15/04 20:12:58.
| */
|
| #ifndef __cplusplus
| #include "stdlib.h"
| #endif
| #include "string.h"
|
| #include "tolua.h"
|
| /* Exported function */
| TOLUA_API int tolua_clasefoo_open (lua_State* tolua_S);
|
| #include "ClaseFoo.h"
|
| /* function to release collected object via destructor */
| #ifdef __cplusplus
|
| static int tolua_collect_ClaseFoo (lua_State* tolua_S)
| {
| ClaseFoo* self = (ClaseFoo*) tolua_tousertype(tolua_S,1,0);
| delete self;
| return 0;
| }
| #endif
|
| /* function to register type */
| static void tolua_reg_types (lua_State* tolua_S)
| {
| tolua_usertype(tolua_S,"ClaseFoo");
| }
|
| /* method: new of class ClaseFoo */
| static int tolua_clasefoo_ClaseFoo_new00(lua_State* tolua_S)
| {
| #ifndef TOLUA_RELEASE
| tolua_Error tolua_err;
| if (!tolua_isusertable(tolua_S,1,"ClaseFoo",0,&tolua_err) ||
| !tolua_isnoobj(tolua_S,2,&tolua_err))
| goto tolua_lerror;
| else
| #endif
| {
| ClaseFoo* tolua_ret = (ClaseFoo*) new ClaseFoo();
| tolua_pushusertype(tolua_S,(void*)tolua_ret,"ClaseFoo");
| }
| return 1;
|
| #ifndef TOLUA_RELEASE
| tolua_lerror:
| tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err);
| return 0;
| #endif
| }
| // ...

En total el archivo anterior posee 216 l铆neas y ser谩 compilando como otro archivo m谩s de nuestro proyecto (por lo que hay que adjuntarlo al mismo ni bien este es generado).

Dentro del archivo anterior existe una funci贸n global que fue escrita y se llama: tolua_clasefoo_open. Esta funci贸n debe ser invocada antes del script que haga referencia a la clase debido a que es all铆 donde se registra el tipo.

Hecho esto, podremos hacer uso de la clase ClaseFoo dentro del script Lua, como en el siguiente ejemplo:


| foo = ClaseFoo:new()
| foo.m_valor = 99
| print(foo:GetValor())

Para que sea posible crear objetos del tipo ClaseFoo dentro de Lua es necesario que la clase ClaseFoo tenga declarado de manera expl铆cita al menos un tipo de constructor.

Entonces, por cada clase que deseemos exponer a lua deberemos crear un archivo pkg que luego deberemos precompilarlo con tolua. Realizar esta operaci贸n manualmente bastante trabajoso y usualmente es recomendable configurar nuestro entorno de programaci贸n para que lo haga por nosotros. Si estamos trabajando con Visual Studio, para ello deberemos seguir los siguientes pasos:

1. Incluir el archivo pkg en nuestro proyecto.

2. Mover el archivo en el panel “Solution Explorer” desde “Solutions Items” a “Source Files”:

3. Acceder a las propiedades del archivo clasefoo.pkg y en la secci贸n “Custom Build Setup” agregar en:

CommandLine: tolua -o $(InputName).cxx $(InputName).pkg

$(InputName) es una variable que representa el nombre del archivo.

Description: Precompilando con ToLua

Aqu铆 podremos especificar la descripci贸n que deseemos (este mensaje aparecer谩 en la ventana “Output” cuando el entorno de desarrollo est茅 trabajando en la construcci贸n del proyecto.

Outputs: $(ProjectDir)/$(InputName).cxx

Indicamos cual ser谩 el nombre y ubicaci贸n del archivo de salida.

4. Precompilado por primera vez el archivo pkg, agregamos su salida al proyecto (el archivo del tipo cxx) para que sea compilado convenientemente.

Compartir:
  • Twitter
  • Facebook
  • MySpace
  • BarraPunto
  • del.icio.us

Pages: 1 2

Comments

  1. Novack
    December 8th, 2007 | 0:40

    Bueno la verdad todavia no he hecho tiempo para leerlos completos, pero ante el esfuerzo, no puedo dejar de comentar :)

    Buen laburo!

Leave a reply