En esta semana he estado estudiando como añadir un sistema de scripting en el proyecto. No tenía experiencia previa, por lo que he tenido que leer bastante documentación al respecto.
En Project Football estamos utilizando como lenguaje principal C++, por lo que cuando se hace cualquier cambio es necesario compilar toda la aplicación para poder probar el efecto de ese cambio. Un sistema de scripting permite realizar determinadas tareas de la aplicación (normalmente la lógica del juego) en un
lenguaje interpretado, por lo que al cambiar estas partes no es necesario volver a compilar la aplicación y se pueden realizar las pruebas inmediatamente.
Tras mirar varias alternativas y consultar a algunas personas (gracias
Roger), finalmente vamos a utilizar
Lua como lenguaje de scripting, en combinación con
Swig para buscar la integración con C++. Si bien el sistema no es muy complejo en cuanto a número de líneas de código (lo podéis ver en la clase "CLuaManager" del directorio "utils"), sí es algo complicado de entender (sobre todo porque mucha de la documentación disponible en Internet está obsoleta).
Para intercambiar información entre Lua y C++ hay que utilizar la pila virtual de Lua, las funciones y sus parámetros se van apilando y desapilando según se van llamando, dejando en la cima de la pila el resultado. Esta interacción se realiza mediante la
API de Lua, y requiere definir expresamente cada una de las funciones de C++ que se quieran utilizar en los scripts mediante esta API.
Esto es un trabajo bastante engorroso si se quieren utilizar muchas funciones de C++, se produce mucho código que hay que mantener, y si se hacen cambios en la interfaces utilizadas de C++ hay que volver a revisar dicho código. Afortunadamente existen herramientas como Swig, que automatizan bastante esta tarea.
La ventaja de Swig frente a otras soluciones es que es una herramienta madura, bien documentada, soporta muchos lenguajes de scripting (entre ellos Lua 5.1), el código generado sólo depende de Lua, y es capaz de leer directamente los ficheros ".h" donde se definen las interfaces de las clases (con algunas pequeñas limitaciones), disminuyendo así la cantidad de código a mantener.
Esto no pretende ser un tutorial de Swig, lo mejor para saber como usar
Swig junto a Lua es en la documentación oficial. Hay que tener cuidado porque algunos ejemplos utilizan C en vez de C++, y la definición de los módulos generados por Swig cambia. En C++ hay que hacer algo similar a esto:
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
int luaopen_nombreModulo(lua_State* L); // declare the wrapped module
}
Esperemos haber acertado en la elección, gracias por la sugerencia
Cuando trate con cosas algo más avanzadas ya lo comentaré por aquí.
Saludos.
Estuve leyendo la doc, pero no saco nada en claro
e = PF.CPfEquipos("6", "1001", "Athletic Bilbao", "ruta/escudo.png");
factoria = PF.CDAOAbstractFactory_getIDAOFactory();
equiposDAO = factoria:getIPfEquiposDAO();
equiposDAO:insert(e);
En el que se crea un objeto de la clase CPfEquipos, se obtiene la instancia de la factoría con la que acceder al DAO de la tabla equipos. Una vez que tengo el DAO puedo realizar cualquier operación que tenga definida sobre la base de datos, en este caso insertar un registro.
Otro ejemplo algo más simple, con el siguiente código accedo a la instancia del Log de la aplicación y llamo a un método suyo:
PF.CLog_getInstance():info("Terminando el script");
En los ejemplos, las clases CLog, CDAOAbstractFactory y la clase que implementa IDAOFactory son Singletons.
src/utils/CLuaManager.h y .cpp
src/utils/CLog.h y .cpp
src/bindings/PF_wrap.cxx
src/tools/swig/create_bindings.sh y los ficheros .i
Para compilar se requiere Lua 5.1. La clase CLog es sólo si quieres usar CLuaManager tal cual, pero puedes substituir las llamadas a CLog por printf's o lo que prefieras. PF_wrap.cxx es el fichero que genera Swig, al ejecutar el script create_bindings.sh, a partir de los ficheros .i.