Poiché l’argomento Lua e C++ è di notevole interesse per me ed ha suscitato un discreto numero di contatti mi sento in dovere di approfondirlo. La cosa necessiterebbe di un mare di tempo e spazio in quanto si potrebbero scrivere libri e libri sull’argomento (e infatti ce ne sono migliaia) ma cercherò di concentrarmi sugli aspetti pratici e implementativi.
Per ogni esempio che farò ora e per i secoli a venire farò sempre riferimento alla mia libreria LuaGrip (che potete scaricare qui). Questa libreria ha lo scopo di nascondere e avvolgere il collegamento di basso livello con la libreria nativa di Lua. Ho fatto questo per due motivi:
- Semplificare e abbreviare le operazioni ripetitive del collegamento c++/lua come ad esempio la creazione dell’ambiente lua e la sua distruzione.
- La libreria di Lua è scritta in C e la sua integrazione in C++ richiede un paio di accortezze che magari risulterebbero di difficile comprensione a chi non è espertissimo. Usando LuaGrip potete fregarvene del tutto e potete utilizzarlo tranquillamente anche se siete apprendisti.
La libreria è direttamente ispirata (dico ispirata per dire che le API sono copiate pari pari) ad una libreria descritta nel libro “Game Developement with Lua” di Paul Schuytema e Mark Manyen. Ho dovuto però reimplementare tutto per rendere l’interfaccia il più generica possibile e per aggiornare il codice alla nuova versione di lua (5.1).
Detto questo, la prima cosa da fare è studiarci per bene il programma di esempio che è incluso nel pacchetto:
#include <string.h>
#include "luagrip.hpp"
Per prima cosa che vediamo sono i consueti include. Fin qui non c’è nulla di particolare.
{
puts("This is Version 2.0 of the Console Program");
return 0;
}
La prima funzione che troviamo è la funzione _Version
. Questa funzione è una funzione LuaGlue ovvero una funzione implementata in C++ che sarà richiamabile da uno script Lua. La funzione ha i seguenti particolari:
- Il valore di ritorno è
LuaGlue
. Consideratelo semplicemente un valore int. - Il nome comincia con un underscore (_). Questa è solo una convenzione stilistica.
- Come unico parametro in ingresso abbiamo un puntatore a
lua_State
.
Tutto questo sembra estremamente limitante ma così non è. Vedremo poi come sia possibile sfruttare il puntatore allo stato (o semplicemente la classe LuaGrip) per avere in ingresso qualsiasi parametro e restituire qualunque valore.
const char *GetCommand(void)
{
printf("Ready> ");
return fgets(gpCommandBuffer,254,stdin);
}
Questa è una semplice funzione ausiliaria, non ha nessun collegamento con lua. Questa funzione non fa altro che leggere una stringa dalla shell e memorizzarla nel buffer gpCommandBuffer
.
{
LuaGrip *pLua = new LuaGrip;
pLua->AddFunction("Version",_Version);
pLua->RunScript("hello.lua");
const char *pCommand = GetCommand();
while(strcmp(pCommand, "QUIT\n") != 0)
{
if(!pLua->RunString(pCommand))
{
printf("ERROR:%s\n",pLua->GetErrorString());
}
pCommand = GetCommand();
}
delete pLua;
}
Ora veniamo al sodo: la funzione main
. La prima cosa che facciamo è creare un istanza della classe LuaGrip che inizializza la macchina virtuale di Lua.
Subito dopo agganciamo la funzione _Version
all’interprete Lua. In pratica diciamo alla macchina virtuale di Lua di agganciare la funzione Lua Version
alla funzione C _Version
. Da questo momento in poi possiamo usare la funzione Version()
all’interno di uno script Lua come se fosse un qualsiasi altro comando Lua.
Subito dopo eseguiamo lo script hello.lua
, script che non fa altro che stampare a schermo una breve introduzione per la nostra console.
A questo punto inizia il ciclo della shell. Leggiamo i comandi inviati dall’utente fino a quando non viene digitato il comando QUIT. I comandi di nostro interesse sono:
pLua->RunString(pCommand)
– Analogamente al comandoRunScript
, il quale esegue uno script contenuto in un file, questo comando esegue comandi Lua contenuti in una stringa.pLua->GetErrorString()
– Questo comando restituisce la stringa di errore generata dall’interprete.
Alla fine non ci resta che eliminare l’istanza di LuaGrip per terminare la macchina virtuale.
Ora siamo pronti a vedere come passare valori e parametri fra C++ e Lua. Ma questo ve lo racconto un’altra volta.